学习笔记之Java笔记

Posted 可持续化发展

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记之Java笔记相关的知识,希望对你有一定的参考价值。

目录

 

重写(Override)与重载(Overload)的区别

接口和抽象类

面向对象的基本特征

String

集合类

1、HashMap 和 Hashtable 的区别?

2、List、Set和Map的特点和区别?集合类概述?

Set:

HashSet

LinkedHashSet

TreeSet

List:

ArrayList和Vector

①ArrayList是list的主要实现类,Vector有很多缺点,不推荐使用。

Arrays.ArrayList

LinkedList

Queue

PriorityQueue

Deque接口、ArrayDeque实现类

Map:

HashMap、HashTable

LinkedHashMap

SortedMap接口、TreeMap实现类

Collections工具类


重写(Override)与重载(Overload)的区别

Java确定一个方法的三要素:①调用者,类还是对象。②方法名。③ 形参列表。方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载主要发生在同一类的多个同名方法之间。重写主要发生在子类和父类的同名方法之间。

方法重载:在同一类中包含了两个或两个以上的方法,其方法名相同,但形参列表不同。重载要求两同一不同:同一个类中方法名相同,形参列表不同(参数的顺序,类型,个数不同)。至于方法的其他部分,如返回值类型、修饰符等,与方法重载无关。重载是让类以统一的方式处理不同类型数据的一种手段。重载是一个类中多态性的一种表现。

方法重写也叫方法覆盖,是一种子类包含与父类同名方法的现象。方法重写遵循“两同两小一大”的原则:“两同”指方法名相同,形参列表相同;“两小”指子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等。“一大”指子类方法的访问权限应比父类方法的访问权限更大或相等。有两点要注意:1、覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法。2、如果父类方法是private权限,则该方法对子类方法是隐藏的,无法重写该方法。

 

接口和抽象类

抽象类是普通类上加abstract,是从多个具体类中抽象出来的父类。抽象类作用:当成父类来被继承。有抽象方法的类只能被定义为抽象类,抽象类中可以没有抽象方法。抽象类可以包含抽象方法,但不能用于创建实例。

接口是从多个相似类中抽象出来的规范,定义了多个类共同的公共行为。接口的好处:让规范和实现分离,降低各个模块的耦合性,为系统提供更好的扩展性和维护性。接口的主要用途:定义变量,可用于进行强制类型转换;调用接口中定义的常量;被其他类实现。接口只能继承接口不能继承类。

1、二者的区别:

①关键字。定义接口用interface,实现接口的关键字为implements,定义抽象类用abstract,继承抽象类的关键字为extends。

②继承和实现。一个类可以实现多个接口,但只能继承一个抽象类。接口可以继承多个接口。接口只能继承接口,不能继承类。接口支持多继承。

③构造器。抽象类可以有构造器。抽象类的构造器不能用于创建实例,主要用于被其子类调用,完成属于抽象类的初始化操作。接口没有构造器。

④成员变量和方法。接口的成员变量只能是静态常量,总是使用public static final修饰,只能在定义时指定默认值。接口的方法只能是抽象方法、类方法、默认方法。接口的抽象方法都是public abstract修饰,类方法都是public static修饰,默认方法都是default修饰。抽象类是普通类加abstract。所以抽象类的成员变量和方法的规则几乎与普通类相同,但多了一个抽象方法(用abstract修饰)。

⑤方法实现。接口中只能包含抽象方法、静态方法、默认方法,不能为普通方法提供方法实现。抽象类可以包含普通方法。

⑥main方法。抽象类可以有main方法并且可以运行它。接口没有main方法。

 

2、二者的共同点:

①都不能被实例化。他们都位于继承树的顶端,用于被其他类实现和继承。

②都可以包含抽象方法。实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

 

3、接口的用途:

①定义变量,可用于强制类型转换。

②调用接口中定义的常量。

③被其他类实现。

 

4、抽象类必须要有抽象方法吗?

抽象类中是允许不包含抽象方法的。抽象类的特性并不是包含抽象方法,而是不允许实例化对象。如果想定义一个类不应该直接被使用,只能再次继承后才可以使用,这时就可以用抽象类了。

 

5、抽象类能使用final修饰吗?

不能。

定义抽象类就是让其他类继承的,如果定义为 final ,该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。

 

面向对象的基本特征

封装:将对象的实现细节隐藏起来,然后通过一些共用的方法暴露该对象的功能。Java提供private、protected、public访问控制修饰符来实现良好的封装。

继承:是面向对象实现软件复用的重要手段,当子类继承父类后,子类作为一种特殊的父类,将直接获得父类的属性和方法,但不能获得父类的构造器。Java提供extends关键字来让子类继承父类。

多态:子类对象可以直接赋给父类变量,但运行时依然表现出子类的行为特征。Java的引用变量有编译时类型(声明变量时使用的类型)和运行时类型(实际赋给该变量的对象类型)。如果编译时类型和运行时类型不一致,就会出现多态。

 

String

1、Java的String类型为什么是不可变的

String类中的成员变量:

private final char value[];

private final int offset;

private final int count;

 

2、不可变的好处

因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。

 

3、比较两个字符串时使用“==”还是equals()方法?

当然是equals方法。==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。equals方法是用于比较两个独立对象的内容是否相同。

如果一个类没有自己定义 equals 方法,它默认的 equals 方法(从 Object 类继承的)就是使用==操作符,也是在比较两个变量指向的对象是否是同一对象,这时候使用equals 和使用==会得到同样的结果,如果比较的是两个独立的对象则总返回 false。如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么就必须覆盖 equals方法,由你自己写代码来决定在什么情况即可认为两个对象的内容是相同的。

 

4、如何将字符串转化成int?

使用包装类Integer。Integer.valueOf("2");

 

5、为什么在Java中存储密码要使用char[],而不使用String。

因为String是不可变的,当String创建之后,就不会再改变了。而char[]是可变的,程序员在用完之后可以将其改变,不会留下任何原始数据。所以,在使用密码时,一般都是会用char数组。

 

6、如何将字符串用空白字符分割开

可以使用正则表达式来做到分割字符。“\\s”代表空白字符” “, “\\t”, “\\r”, “\\n”等等, String[] strArray = str.split("\\\\s+");

 

7、String常用方法

equals():字符串比较。

length():返回字符串长度。

indexOf():返回指定字符第一次出现的索引。

charAt():返回指定索引处的字符。

toLowerCase():将字符串转成小写字母。

toUpperCase():将字符串转成大写字符。

split():分割字符串,返回一个分割后的字符串数组。

substring():截取字符串。

replace():支持字符和字符串的替换。

replaceAll():基于正则表达式的字符串替换。

trim():去除字符串两端空白。

getBytes():返回字符串的 byte 类型数组。

 

8、Java中操作字符串都有哪些类?它们之间有什么区别?

Java中操作字符串的类有3个,String,StringBuilder,StringBuffer。在核心上字符串就是字符的数组,String、StingBuilder、StringBuffer的源代码中都有一个私有变量保存着字符数组。不同之处只在于性能和线程安全性问题。String的性能最差,StringBuilder的性能最好,StringBuffer相对StringBuilder慢一些。

String对象是不可变对象,所以性能最差。不可变对象的意思是对象的值是不可变的,你定义了一个String对象值为abc,就无法再改变值内容了,改变值内容其实是创建了一个新对象。

StringBuilder性能最好,但线程不安全.在StringBuilder内部管理一个字符数组来存储字符串内容,如果对字符串变更后的长度不大于字符数组长度,就直接在当前字符数组上进行操作;如果新的字符串长度超过数组长度的话,就用新创建的字符数组替换原有的字符数组,新字符数组的长度为当前字符数组的两倍,并将字符串内容写入到字符数组中。大多数情况下,都是在已经开辟的字符数组空间中进行内存操作,不会创建新的对象,性能得到极大提高。并且StringBuilder在设计的时候并不考虑多线程并发问题,因此代码更加精简,性能是最好的。

StringBuffer机制与StringBuilder基本一样,但性能略差,因为StringBuffer类的成员方法前面多了synchronized。StringBuffer相对StringBuilder性能差了一点点,但相应的对于多线程场景下,StringBuffer的稳定性和容错性要好的多。

 

9、String str="hello world"和String str=new String("hello world")的区别。

String str="hello world";在编译期间生成了字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池。通过new关键字来生成对象是在Java堆进行的,对象在Java堆中分配内存。通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。

10. 下面这段代码的输出结果是什么?

String a = "hello2";   

String b = "hello" + 2;   

System.out.println((a == b));

  输出结果为:true。原因很简单,"hello"+2在编译期间就已经被优化成"hello2",因此在运行期间,变量a和变量b指向的是同一个对象。

 

11.下面这段代码的输出结果是什么?

String a = "hello2";    

String b = "hello";       

String c = b + 2;       

System.out.println((a == c));

  输出结果为:false。由于有符号引用的存在,所以  String c = b + 2;不会在编译期间被优化,不会把b+2当做字面常量来处理的,因此这种方式生成的对象事实上是保存在堆上的。因此a和c指向的并不是同一个对象。

 

12、下面这段代码的输出结果是什么?

String a = "hello2";    

final String b = "hello";       

String c = b + 2;       

System.out.println((a == c));

  输出结果为:true。对于被final修饰的变量,会在class文件常量池中保存一个副本,也就是说不会通过连接而进行访问,对final变量的访问在编译期间都会直接被替代为真实的值。那么String c = b + 2;在编译期间就会被优化成:String c = "hello" + 2;

 

集合类

1、HashMap 和 Hashtable 的区别?

HashMap 是 Hashtable 的轻量级实现(非线程安全的实现),他们都实现 Map 接口,主要区别在于 HashMap 允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于 Hashtable。

HashMap 允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsvalue 和 containsKey。因为contains 方法容易让人引起误解。

Hashtable 继承自 Dictionary 类,而 HashMap 是 Java1.2 引进的 Map interface 的一个实现。

最大的不同是,Hashtable 的方法是 synchronized 的,而 HashMap 不是,在多个线程访问Hashtable 时,不需要自己为它的方法实现同步,而 HashMap 就必须为之提供外同步。

Hashtable 和 HashMap 采用的 hash/rehash 算法都大概一样,所以性能不会有很大的差异。

 

2、List、Set和Map的特点和区别?集合类概述?

Java的集合类主要有Collection和Map接口派生出来。它们是集合框架的根接口。所有集合类都位于java.util包下。Set、List、Queue是Collection的子接口。

collection集合中常用的共性方法有7个:

1 public boolean add(E e):添加一个元素

2 public void clear():清空集合中所有的元素

3 public boolean remove(E e):移除集合中指定的元素

4 public boolean contains(E e):检查集合中是否包含指定的对象

5 public boolean isEmpty():判断集合是否为空

6 public void size():判断集合中元素的个数

7 public Object[] toArray():把集合元素存储到数组中

Set:

Set是collection的子接口。Set与Collection基本相同,没有提供额外的方法,只是Set不允许包含重复元素。

HashSet、LinkedHashSet和TreeSet

------------------------------------- 

HashSet

HashSet是Set的实现。HashSet用hash算法存储元素,存取和查找的性能好。HashSet的特点:

①不能保证元素排列顺序;②HashSet不是同步的。③元素可以是null。

 

向HashSet插入一个元素时,HashSet会调用该对象的hashcode()方法来获得hashcode值,然后根据hashcode值决定该对象在HashSet中的存储位置。

HashSet判断两个元素是否相等的标准是两个对象通过equals()方法比较相等,并且他们的hashcode值也相等。

当把一个对象放入HashSet中,如果要重写该对象对应类的equals()方法,则也应该重写其hashcode()方法。因为Set集合规则是:如果两个对象通过equals()方法比较返回true,则这两个对象的hashcode值也应该相同,即这两个对象相同。

如果两个对象通过equals()比较返回true,但他们的hashcode不同,这样HashSet会将他们存放在不同的位置,添加成功,这就与Set集合规则冲突了。如果他们hashcode值相同,但equals()方法返回false,则会在对应的位置使用链式结构保存多个对象,但这样性能会下降。

重写hashcode()方法的基本规则:

①同一个对象多次调用hashcode()方法应该返回相同的值。

②当两个对象通过equals()方法返回true时,他们的hashcode值也应该相同。

③对象中用作equals()方法比较标准的实例变量,都应该用于计算hashcode值。

 

为什么不直接用数组而用HashSet?

因为数组元素的索引是连续的,数组的长度是固定的,无法自由增加数组长度。而HashSet根据元素的hashcode值确定存储位置,从而可以自由增加HashSet的长度,还可以根据元素的hashcode值来访问元素。所以,HashSet访问元素时,HashSet会先计算该元素的hashcode值,然后直接到该hashcode值对应的位置去取出该元素,这也是HashSet速度快的原因。

------------------------------

LinkedHashSet

它是HashSet的一个子类,根据元素的hashcode值决定元素的存储位置,但它同时用链表维护元素的次序。遍历LinkedHashSet时,会按元素的添加顺序来输出元素。LinkedHashSet也是HashSet,不允许集合元素重复。

------------------------------ 

TreeSet

TreeSet是SortedSet接口的实现类,TreeSet可以保证元素处于排序状态。与HashSet相比,额外的方法有:

1、comparator():如果TreeSet用了定制排序,则该方法返回对应的comparator。如果采用自然排序,则返回null。

2、first():返回集合中第一个元素。

3、last():返回最后一个元素。

4、lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素)

5、higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素)

6、subSet(Object fromElement,Object toElement):返回此Set的子集合,范围从fromElement(包含)到toElement(不包含)

7、headSet(Object toElement):返回子集,由小于toElement的元素组成

8、tailSet(Object fromElement):返回子集,由大于或等于fromElement的元素组成。

 

HashSet用hash算法决定元素的存储位置,TreeSet用红黑树存储元素。

建议不要修改放入TreeSet和HashSet中元素的关键实例变量,否则容易出错。TreeSet可以删除没有被修改实例变量、且不与其他被修改实例变量的对象重复的对象,否则删除失败。

 

TreeSet的自然排序

TreeSet会调用元素的compareTo(Object e)方法来比较元素之间的大小,将元素按升序排列。TreeSet自然排序判断两个对象是否相等的标准是:两个对象通过compareTo()方法比较是否返回0---返回0,就是相等的;否则,不相等。

自然排序时,如果试图把一个对象添加到TreeSet中,该对象的类必须实现Comparable接口,否则,异常。自然排序时,向TreeSet中添加的应该是同一个类的对象,否则会异常:ClassCastException。因为添加元素时,TreeSet会调用该对象的compareTo()方法和集合中的其他元素进行比较,这就要求二者应该是同一个类的实例。只有相同类的两个实例才能比较大小。

当把对象存入TreeSet中,重写对象对应类的equals()方法时,要保证该方法和compareTo()方法有一致的结果,规则是:如果两个对象通过equals返回true,则他们通过compareTo()方法也应该返回0。

Comparable接口中有一个compareTo()方法,其返回一个整数值。实现该接口必须实现该方法。obj1.compareTo(obj2):如果返回0,则两个对象相等;返回正整数,则obj1大于obj2;返回负数,则obj1小于obj2。实现了Comparable接口的类有:①BigDecimal、BigInteger和所有数值型的包装类;②Character;③Boolean;④String;⑤Date、Time。

 

TreeSet的定制排序

可以通过comparator接口。该接口里有一个compare(T o1,T o2)方法,返回正整数,o1大于o2;返回0,o1等于o2;返回负整数,o1小于o2。在创建TreeSet集合对象时,可以用Lambda表达式来代替comparator对象。

定制排序需要类型相同的对象,TreeSet由comparator对象负责排序规则。判断两个元素是否相等的标准是:通过comparator比较是否返回了0。

 

性能分析:

HashSet性能比TreeSet好(特别是在添加、查询元素时)。因为TreeSet需要额外的红黑树算法来维护元素的次序。当需要保持排序的Set时,才用TreeSet,否则,用HashSet。对于插入、删除操作,LinkedHashSet比HashSet要慢,因为LinkedHashSet要维护链表。但遍历LinkedHashSet比遍历HashSet要快。

HashSet和TreeSet都是线程不安全的,可用Collections工具类的synchronizedSortedSet方法在创建时包装该Set集合。

 

 

 

List:

List是Collection的子接口。List的特点是:①元素有序,可重复;②每个元素都有对应的顺序索引,可通过索引访问指定位置的集合元素。默认按照元素的添加顺序来设置元素索引。相比collection,多了些额外方法:

1、add(int index, Object element):将元素插入list中的index处。

2、addAll(int index, Collection c):将集合c中所有的元素都插入list的index处。

3、remove(int index):删除并返回index处的元素。

4、set(int index,Object element):将index处的元素替换成element,并返回旧元素。

5、get(int index):返回集合index索引处的元素。

6、indexOf(Object o):返回对象o在list中第一次出现的位置索引。

7、lastIndexOf(Object o):返回最后一次出现的位置索引。

8、subList(int fromElement,int toElement):返回从fromElement(包含)到toElement(不包含)的子集合。

9、sort(Comparator c):根据Comparator参数对list元素排序。

10、replaceAll(UnaryOperator operator):根据新的计算规则,重新设置list中的所有元素。

 

List判断元素相等的标准是:只要通过equals()方法返回true即可。List变量.remove(Object A)时,list会调用A对象的equals()方法和集合元素依次进行比较。如果返回true了,就删除。

------------------------------------- 

ArrayList和Vector

他们是List的实现类。ArrayList和Vector都是基于数组实现的List类,都封装了一个动态的、允许再分配的Object[]数组。他们的对象使用initial Capacity参数来设置数组长度,当添加元素超过数组长度时,initial Capacity会自动增加。可以在创建时指定initial Capacity;如果不指定,数组的默认长度为10。额外方法:

1、(用于添加大量元素时,可减少重分配次数,增加性能)ensureCapacity(int minCapacity):将ArrayList或Vector的数组长度增加大于或等于minCapacity的值。

2、(用于减少集合对象占用的存储空间)trimToSize():调整ArrayList或Vector的数组长度为当前元素的个数。

 

ArrayList的底层原理:

ArrayList是基于数组实现的List类,都封装了一个动态的、允许再分配的Object[]数组。ArrayList用名为elementData的成员变量来存储元素, elementData是一个用transient 修饰的Object[]数组。

transient Object[] elementData;

transient的作用是是让被修饰的成员变量不被序列化。

ArrayList有三个构造器:

①第一个是无参构造器。将elementData设为了空数组。

②第二个构造器的参数为initialCapacity。根据initialCapacity参数来创建Object[]数组。

③第三个构造器的参数是Collection对象。将collection集合中的元素转为ArrayList。最后会调用Arrays.copyOf(elementData, size, Object[].class);方法。

 

 

 

ArrayList的扩容机制:

ArrayList的默认容量为10,一次扩容后是容量为15。扩容增量是:原容量的 1.5倍。ArrayList的扩容方法grow,最终调用了Arrays.copyOf(elementData, newCapacity)方法进行扩容。

 

Vector的扩容机制:

Vector的默认容量为10,一次扩容后是容量为20。扩容增量是:原容量的 1倍。

 

ArrayList和Array的区别:

①概念不同。Array是数组,它的初始化必须指定大小,且创建后的数组大小是固定的。而ArrayList是基于数组实现的List类,都封装了一个动态的、允许再分配的Object[]数组。

②存放数据的不同。Array可以存放对象类型的数据、基本数据类型的数据。而ArrayList只能存放对象数据类型的数据

③使用方法的不同。Array数组只能通过数组下标来对指定位置的元素进行访问。而ArrayList在Array的基础上增加了很多的方法。比如add,addAll,remove,removeAll,contains等等。

④效率不同。ArrayList的效率不如Array的高。

 

ArrayList和Vector的区别:

①ArrayList是list的主要实现类,Vector有很多缺点,不推荐使用。

②ArrayList是线程不安全的,Vector是线程安全的。所以,Vector的性能比ArrayList低。可以用Collections工具类让ArrayList线程安全。

------------------------------------ 

Arrays.ArrayList

Arrays工具类中有个asList(…)方法,能把一个数组或指定个数对象转换为一个List集合。这个List集合不是ArrayList或Vector的实例,而是Arrays内部类ArrayList的实例。Arrays.ArrayList是一个固定长度的List集合,程序只能遍历元素,不能增加、删除元素。

------------------------------------

LinkedList

LinkedList是List接口的实现类,可以用索引来随机访问元素,而且还实现了Deque接口,可以当做双端队列,栈,队列。

 

性能分析:

List是一个线性接口。ArrayList基于数组的线性表。LinkedList是基于链表的线性表。一般,由于数组以连续的内存区保存数组元素,所以数组在随机访问时性能最好,所有内部以数组作为底层实现的集合在随机访问元素时性能都比较好。而内部以链表作为底层实现的集合在执行插入、删除操作时性能更好。但总体来说,ArrayList比LinkedList性能好。

List集合使用建议:

①如果要遍历集合元素,对于ArrayList、Vector,应该使用随机访问方法(get)来遍历。对于LinkedList,应该采用迭代器来遍历。

②如果需要经常执行插入、删除操作来改变包含大量数据的List集合大小,可考虑使用LinkedList。如果用ArrayList、Vector,可能会经常重分配数组,效果差。

③多线程访问,用Collections工具类包装。

 

Queue

Queue用于模拟队列,通常“先进先出”。队列头部放存放时间最长的元素,尾部放存放时间最短的元素。通常队列不允许随机访问队列中的元素。Queue接口的方法:

1、add(Object e):将元素加入队尾。

2、offer(Object e):将元素加入队尾。当用有限容量的队列时,此方法比add(…)方法好。

3、remove():获取并删除队头元素。

4、poll():获取并删除队头元素。如果队列为空,则返回null。

5、peek():获取但不删除队头元素。如果队列为空,则返回null。

6、element():获取但不删除队头元素。

 

------------------------------------

PriorityQueue

PriorityQueue是Queue的实现类。PriorityQueue保存元素的顺序是按队列元素的大小进行重新排序的,不是按加入队列的顺序。所以,当调用peek()或poll()方法时取出的是队列中最小的元素,而不是最先进入的。PriorityQueue不允许插入null,需要对元素排序。2种排序方式:自然排序和定制排序。和TreeSet排序要求一样。自然排序:要求元素实现comparable接口,且元素应该是同一个类的多个实例,否则可能异常。定制排序:要求在创建时传入一个comparator对象,不要求元素实现comparable接口。

------------------------------------

Deque接口、ArrayDeque实现类

Deque接口是Queue接口的子接口,代表一个双端队列,常用方法:

1、addFirst(Object e):插入队头

2、addLast(Object e):插入队尾

3、offerFirst(Object e):插入队头

4、offerLast(Object e):插入队尾

5、pollFirst():获取并删除队头。如果队列为空,返回null

6、pollLast():获取并删除队尾。如果队列为空,返回null

7、removeFirst():获取并删除队头

8、removeLast():获取并删除队尾

9、removeFirstOccurrence(Object o):删除队列中第一次出现的元素o

10、removeLastOccurrence(Object o):删除队列中最后一次出现的元素o

11、getFirst():获取但不删除队头

12、getLast():获取但不删除队尾

13、peekFirst():获取但不删除队头。如果队列为空,返回null

14、peekLast():获取但不删除队尾。如果队列为空,返回null

15、(栈方法)push(Object e):将元素push进双端队列所代表的栈的栈顶,相当于addFirst(e)。

16、(栈方法)pop():pop出双端队列所表示的栈的栈顶,相当于removeFirst()。

ArrayDeque是Deque接口的实现类,是基于数组实现的双端队列。Deque底层数组的长度为16。ArrayList和ArrayDeque底层都是一个动态的、可重分配的Object[]数组。当元素超过容量时,系统会自动在底层重新分配一个Object[]数组来存元素。

 

Map:

Map用于保存具有映射关系的数据,有时被称为字典或关联数组。所以,Map集合的元素以key-value的形式存在。其中key不允许重复,即两个key通过equals()方法比较总返回false。Map提供了一个内部类Entry来封装key-value对。计算Entry存储时只考虑Entry封装的key。如果把Map里的所有key放在一起看,他们就组成了一个Set集合(key无序,且不重复)。Map子类和Set子类在名字上很相似:Set接口下有HashSet、LinkedHashSet、SortedSet、TreeSet;Map接口下有HashMap、LinkedHashMap、SortedMap、TreeMap。

从源码来看,Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合。Map通过key索引来提取元素。常用方法:

1、put(Object key,Object value):添加一个key-value对。如果Map中已经有一个与该key相等的key-value对,则新的会覆盖旧的。

2、putAll(Map m):将指定Map中的key-value对复制到本Map中。

3、clear():删除所有的key-value对

4、remove(Object key):删除指定key对应的key-value对,并返回被删除key所对应的value。如果key不存在,则返回null。

5、remove(Object key,Object value):删除指定key-value对。如果成功,则返回true,否则,false。

6、replace(…):替换key-value对中的内容

7、containsKey(Object key):查询Map是否包含指定key,有,就返回true

8、containsValue(Object value):查询Map是否包含指定value,有,就返回true。

9、entrySet():返回所有key-value对所组成的Set集合。

10、keySet():返回所有key所组成的Set集合。

11、get(Object key):返回指定key对应的value。如果Map中没有这样的key,则返回null。该方法的原理:先计算参数key的hashcode值,再将参数key和其hashcode值传入hashmap类的getNode方法,getNode方法会找出Map中与参数key的hashcode值相等且二者通过equals方法返回true的内部类Node对象。如果找到了,就返回Node的value,否则,就返回null。

 

 

  

12、size():返回key-value对的个数。

----------------------------------------- 

Map的内部类Entry,该类封装了一个key-value对,有三个方法:getKey()、getValue()、setValue(V value)

-----------------------------------------

HashMap、HashTable

HashMap和HashTable的关系类似于ArrayList和Vector的关系。HashTable是古老的Map实现类。Java8改进了HashMap的实现,使用HashMap存在key冲突时依然具有较好的性能。

二者的区别:

①HashTable是一个线程安全的Map实现,HashMap是线程不安全的。所以,HashMap比HashTable的性能高;但如果是多线程访问Map,HashTable会更好。多线程情景中,推荐用Collections工具类包装HashMap。

②HashTable不允许使用null作为key和value。HashMap允许使用null作为key或value。

二者的相同点:

①HashMap和HashTable都不能保证key-value对的顺序。

②判断两个key相同的标准一样:两个key通过equals()方法返回true且它们的hashcode值相等。

③判断两个value相等的标准一样:两个对象通过equals()方法比较返回true,就是相等的。

尽量不要使用可变对象作为HashTable、HashMap的key。如果真的要用,就尽量不要在程序中修改作为key的可变对象。如果修改了作为key的可变对象,则程序再也无法准确访问到Map中被准确修改的key。

当使用自定义类作为HashTable和HashMap的key时,如果重写该类的equals()方法和hashCode()方法,则应该保证这两个方法的判断标准一致:当两个key通过equals()方法返回true时,他们的hashcode值也应该相同。因为HashTable和HashMap保存key的方式与HashSet保存元素的方式完全相同。

 

HashMap的Hash算法

hash算法就是通过hashcode与自己右移16进行异或运算。这样做是为了计算出来的hash值足够随机,足够分散,还有产生的数组下标足够随机。

 

HashMap的底层原理

在JDK1.8之前HashMap是数组+链表的形式,JDK1.8包括之后是数组+链表+红黑树。

HashMap封装了一个静态内部类Node。Node类实现了Map.Entry接口。HashMap的默认负载因子是0.75f,默认容量是16。HashMap有三个构造器:

①无参构造器。②参数为initialCapacity。③参数为initialCapacity和loadFactor。

HashMap的 put 的工作原理大致可以分为:

1、对key值进行Hash值计算(hash方法),然后再在计算下标。

2、如果key对象在桶中不存在,则直接放在桶中。

3、如果key对象的hash值的对应位置有对象存在但这两个key不相同,则会以链表的方式存储,链接在后面。

4、如果链表的长度超过了阈值(TREEIFY THRESHOLD==8),就会把链表转换为红黑树,链表长度低于6,就把红黑树转换为链表。

5、如果节点存在,就会替换旧值。

6、如果桶满(就是容量16*加载因子0.75),就会resize(扩容2倍,再重排)。

 

HashMap的 get 获取对象的工作原理为:

get的作用是:返回指定key对应的value。如果HashMap中没有这样的key,则返回null。该方法的原理:先计算参数key的hashcode值,再将参数key和其hashcode值传入hashmap类的getNode方法,getNode方法会找出Map中与参数key的hashcode值相等且二者通过equals方法返回true的内部类Node对象。如果找到了,就返回Node的value,否则,就返回null。

 

------------------------------------- 

LinkedHashMap

LinkedHashMap是HashMap的子类。LinkedHashMap使用双向链表来维护key-value对的次序,该链表保证迭代顺序与key-value对的插入顺序保持一致。

------------------------------------- 

SortedMap接口、TreeMap实现类

TreeMap是一个红黑树数据结构。每个key-value对为一个节点。根据key对节点进行排序。TreeMap保证所有key-value对处于有序状态。

自然排序:TreeMap的所有key应该是同一个类的对象,而且都要实现comparable接口,否则,异常。如果使用自定义类作为TreeMap的key,则重写该类的equals()方法和compareTo()方法时应该保持一致的返回结果:两个key通过equals()方法比较返回true时,它们通过compareTo()方法比较应该返回0。如果equals()方法和compareTo()方法的返回结果不一致,TreeMap和Map接口的规则就会冲突。

定制排序:创建TreeMap时,传入comparator对象。该对象负责对所有的key进行排序。不要求key实现comparable接口。

------------------------------------ 

性能分析:

对于一般情景,多考虑HashMap,因为HashMap是为快速查询而设计的(HashMap底层是采用数组来存储key-value对)。但如果程序需要一个总是排好序的Map时,则考虑TreeMap。LinkedHashMap比HashMap慢一点。因为它需要维护链表来保持元素的添加顺序。

 

HashSet和HashMap的性能选项

对于HashSet,它是用hash算法来决定元素的存储位置,并通过hash算法来控制集合的大小。对于HashMap和HashTable,它们通过hash算法来决定Map中key的存储,并通过hash算法来增加key集合的大小。

 

 

------------------------------------ 

 

Collections工具类

常用方法:

排序操作

1、reverse(List list):反转list中元素顺序。

2、shuffle(List list):对list的元素进行随机排序。

3、sort(…):自然排序或定制排序

4、swap(List list,int i,int j):将list中的i处元素和j处元素进行交换。

 

查找、替换操作

1、binarySearch(List list, Object key):用二分查找搜索指定key,前提是必须保证List中的元素已经处于有序状态。

2、max(…):返回最大元素,有自然排序和定制排序

3、min(…):返回最小元素,有自然排序和定制排序

同步控制操作

多个synchronizedXxx()方法,把集合包装成线程同步的。

 

 

以上是关于学习笔记之Java笔记的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记之Java笔记

Java学习笔记之:Java数组

Java学习笔记之:java的数据类型

Java学习笔记之:Java简介

Java学习笔记之:java数据类型的转换

java学习笔记之面向对象