疯狂java讲义总结-NO8
1. Java集合大致可分为Set,List,Queue和Map四种体系,其中Set代表无序、不可重复的集合;List代表有序、可重复的集合;而Map则代表具有映射关系的集合(也被称为关联数组),Java 5又增加了Queue体系集合,代表一种队列集合实现。
2. Map里的key是不可以重复的,key用于标识集合里的每项数据,如果需要查阅Map中的数据时,总是根据Map的key来获取。
3. 在传统模式下,把一个对象“丢进”集合中后,集合会忘记这个对象的类型,也就是说,系统把所有的集合元素都当成Object类型。从JDK 1.5以后,这种状态得到了改进:可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型。
4.(1)boolean add(Object o)
(2)boolean addAll(Collection c)
(3)void clear()
(4)boolean contains(Object o)
(5)boolean containsAll(Collection c)
(6)boolean isEmpty()
(7)Iterator iterate()
(8)boolean remove(Object o)
(9)boolean removeAll(Collection c)
(10)boolean retainAll(Collection c)
(11)int size()
(12)Object[] toArray()
5.虽然集合里面不能放基本类型的值,但是java支持自动装箱。
6. HashSet具有以下特点:
(1)不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
(2)HashSet不是同步的,如果多个线程同时访问一个HashSet.假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步。
(3)集合元素值可以是null
7. HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。
8. 当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode()方法。规则是:如果两个对象通过equals()方法比较返回true,这两个对象的hashCode值也应该相同。
9. 当从HashSet中访问元素时,HashSet先计算该元素的hashCode值(也就是调用该对象的hashCode()方法的返回值),然后直接到该hashCode值对应的位置去取止该元素----—这就是HashSet速度很快的原因。
10. HashSet中每个能存储元素的“槽位”(slot)通常称为“桶”(bucket),如果有多个元素的hashCode值相同,但它们通过equals()方法比较返回false,就需要在一个”桶“里放多个元素,这样会导致性能下降。
11. 重写hashCode()方法的基本规则:
(1)在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。
(2)当两个对象通过equals()方法比较返回true时,这两个对象的hashCode()方法应该返回相同的值。
(3)对象中用作equals()方法比较标准的实例变量,都应该用于计算hashCode值。
12. HashSet还有一个一子类LinkedHashSet, LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元索。
当输出LinkedHashSet集合的元素时,元素的顺序总是与添加顺序一致,但它依然不允许集合元素重复。
13. TreeSet是SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态。
14. TreeSet并不是根据元素的插入顺序进行排序的,而是根据元素实际值的大小来进行排序的。
15. 与HashSet集合采用hash算法来决定元索的存储位置不同,TreeSet采用红黑树的数据结构来存储集合元素。那么TreeSet进行排序的规则是怎样的呢?TreeSet支持两种排序方法:自然排序和定制排序。在默认情况下,TreeSet采用自然排序。
TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式就是自然排序。
16. 向TreeSet集合中添加元素时,只有第一个元素无须实现Comparable接口,后面添加的所有元素都必须实现Comparable接口。当然这也不是一种好做法,当试图从TreeSet中取出元素时,依然会引发CIassCastException异常。
17. 如果希望TreeSet能正常运作,TreeSet只能添加同一种类型的对象。
18. 当通过Comparator对象(或Lambda表达式)来实现TreeSet的定制排序时,依然不可以向TreeSet中添加类型不同的对象,否则会引发C1assCastException异常。使用定制排序时,treeSet对集合元素排序不管集合元素本身的大小,而是由Comparator对象(或Lambda表达式)负责集合元素的排序规则。TreeSet判断两个集合元素相等的标准是:通过Comparator(或Lambda表达式)比较两个元素返回了0,这样TreeSet不会把第二个元素添加到集合中。
19. EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元索都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。
20. EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好。
21. EnumSet集合不允许加入null元素,如果试图插入null元素,EnumSet将抛出NuIlPointerException异常。
22. 当复制Collection集合中的所有元素来创建新的EnumSet集合时,要求Collection
集合中的所有元索必须是同一个枚举类的枚举值。
23. HashSet和TreeSet是Set的两个典型实现,到底如何选择HashSet和TreeSet呢?HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
24. HashSet还有一个子类:LinkedHashSet,对于普通的插入、删除操作,LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashSet会更快。
25. EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
26. 必须指出的是,Set的三个实现类HashSet, TreeSet和EnumSet都是线程不安全的。如果有多个线程同时访问一个Set集合,并且有超过一个线程修改了该Set集合,则必须手动保证该Set集合的同步性。通常可以通过Collections工具类的synchronizedSortedSet方法来“包装”该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问。
27. List判断两个对象相等只要通过equals()方法比较返回true即可。
28. 当调用List的set(int index, Object element)方法来改变List集合指定索引处的元素时,指定的索引必须是List集合的有效索引、例如集合长度是4,就不能指定替换索引为4处的元素也就是说,set(int index, Object element)方法不会改变List集合的长度。
29. ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类封装了一个动态的、允许再分配的Object[]数组。ArrayList或Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超出了该数组的长度时,它们的initialCapacity会自动增加。
30. 如果创建空的ArrayList或Vector集合时不指定initialCapacity参数,则Object[]数
组的长度默认为10。
31.void trimToSize():调整ArrayList或Vector集合的Object[]数组长度为当前元素个数。
32.ArrayList是线程不安全的,但Vector集合则是线程安全的。
33.Stack继承了Vector,因此它也是一个非常古老的java集合类,他同样是线程安全的、性能较差的,因此应该尽量少用Stack类。
34. 操作数组的工具类:Arrays,该工具类里提供了asList(Object... a)方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例。
35. Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不可增加、删除该集合里的元素。
36. 如果试图增加、删除Arrays$ArrayList集合里的元素,将会引发UnsupportedOperationException异常。
37.通常队列不允许随机访问队列中的元素。
38. Queue接口有一个PriorityQueue实现类。除此之外,Queue还有一个Deque接口,Deque代表一个“双端队列”,双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可当成队列使用,也可以当成栈使用。Java为Deque提供了ArrayDeque和LinkedList两个实现类。
39. PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。
40. 运行程序直接输出PriorityQueue集合时,可能看到该队列里的元素并没有很好地按大小进行排序,但这只是受到PriorityQueue的toString()方法的返回值的影响。实际上,程序多次调用PriorityQueue集合对象的poll()方法,即可看到元素按从小到大的顺序“移出队列”。
41. PriorityQueue不允许插入null元素,它还需要对队列元素进行排序。
42.Deque底层数组长度为16.
43. LinkedList类是List接口的实现类—---这意味着它是一个List集合,可以根据索引来随机访问集合中的元素。除此之外,LinkedList还实现了Deque接口,可以被当成双端队列来使用,因此既可以被当成“栈”来使用,也可以当成队列使用。
44. LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入、删除元素时性能比较出色(只需改变指针所指的地址即可)。
45.对于所有的内部基于数组的集合实现,例如ArrayList, ArrayDeque等,使用随机访问的性能比使用Iterator迭代访问的性能要好,因为随机访问会被映射成对数组元素的访问。
46. (1)如果需要遍历List集合元素,对于ArrayList、Vector集合,应该使用随机访问方法(get)来遍历集合元素,这样性能更好;对于LinkedList集合,则应该采用迭代器(Iterator)来遍历集合元素。
(2)如果需要经常执行插入、删除操作来改变包含大量数据的List集合的大小,可考虑使用LinkedList集合,使用ArrayList、Vector集合可能需要经常重新分配内部数组的大小,效果可能较差。
(3)如果有多个线程需要同时访问List集合中的元索,开发者可考虑使用Collections将集合包装成线程安全的集合。
47.Map的key不允许重复,key和value之间存在一一对应关系。
48. Map提供了一个Entry内部类来封装key-value对,而计算Entry存储时则只考虑Entry封装的key。从Java源码来看,Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合。
49.Map有时也被称为字典或者关联数组。
50.Set keyset():返回该Map中所有key的Set集合。
51.Colleation values():返回该Map中所有value组成的Collection。
52.HashMap和Hashtable的区别:
(1)Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能高一点:但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好。
(2)Hashtable不允许使用null作为key和value,如果试图把null值放进Hashtable中,将会引发NulIPointerException异常:但HashMap可以使用null作为key或value。
53. 由于HashMap里的key不能重复,所以HashMap里最多只有一个key-value对的key为null,但可以有无数多个key-value对的value为null。
54. HashMap, Hashtable判断两个value相等的标准更简单:只要两个对象通过equals()方法比较返回true即可。
55. 与HashSet类似的是、尽量不要使用可变对象作为HashMap, Hashtable的key,如果确实需要使用可变对象作为HashMap. Hashtable的key,则尽量不要在程序中修改作为key的可变对象。
56. 迭代输出LinkedHashMap的元素时,将会按添加key-value对的顺序输出。
57. Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的“属性名一属性值”加载到Map对象中。
58.(1)void load(InputStream inStream):从属性文件(以输入流表示)中加载key-value对,把加载到的key-value对追加到Properties里( Properties是Hashtable的子类,它不保证key-value之间的次序)。
(2)void store(OutputStream out,String comments):将Properties中的key-value对输出到指定的属性文件(以输出流表示)中。
59. Properties可以把key-value对以XML文件的形式保存起来,也可以从XML文件中加载key-value对。
60. TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态。
61. 类似于TreeSet中判断两个元素相等的标准,TreeMap中判断两个key相等的标准是:两个key通过compareTo()方法返回0, TreeMap即认为这两个key是相等的。
62. 如果需要使用WeakHashMap的key来保留对象的弱引用,则不要让该key所引用的对象具有任何强引用,否则将失去使用WeakHashMap的意义。
63. IdentityHashMap捉供了与HashMap基本相似的方法,也允许使用null作为key和value。
64. EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。
65.EnumMap具有如下特征:
(1)EnumMap在内部以数组形式保存,所以这种实现形式非常紧凑、高效。
(2)EnumMap根据key的自然顺序(即枚举值在枚举类中的定义顺序)来维护key-value对的顺序。当程序通过keySet(), entrySet(), values()等方法遍历EnumMap时可以看到这种顺序。
(3)EnumMap不允许使用null作为key,但允许使用null作为value。如果试图使用null作为key时将抛出NulIPointerException异常。如果只是查询是否包含值为null的key,或只是删除值为null的key,都不会抛出异常。
66. 与创建普通的Map有所区别的是,创建EnumMap时必须指定一个枚举类,从而将该EnumMap和指定枚举类关联起来。
67.各Map实现类的性能分析:
(1)对于Map的常用实现类而言,虽然HashMap 和Hashtable的实现机制几乎一样,但由于Hashtable时一个古老的、线程安全的集合,因此HashMap通常比Hashtable要快。
(2)TreeMap通常比HashMap、 Hashtable要慢(尤其在插入、删除key-value对时更慢),因为TreeMap底层采用红黑树来管理key-value对(红黑树的每个节点就是一个key-value对)。
(3)使用TreeMap有一个好处:TreeMap中的的key-value对总是处于有序状态,无须专门进行排序操作。当TreeMap被填充之后,就可以调用keySet(),取得由key组成的Set,然后使用toArray()方法生成key的数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速地查询对象。
(4)对于一般的应用场景,程序应该多考虑使用HashMap,因为HashMap正是为快速查询设计的( HashMap底层其实也是采用数组来存储key-value对)。但如果程序需要一个总是排好序的Map时,则可以考虑使用TreeMap。
(5)LinkedHashMap比HashMap慢一点,因为它需要维护链表来保持Map中key-value时的添加顺序。IdentityHashMap性能没有特别出色之处,因为它采用与HashMap基本相似的实现,只是它使用==而不是equals()方法来判断元素相等。EnumMap的性能最好,但它只能使用同一个枚举类的枚举值作为key。
68. 当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为:rehashing。
69. HashSet和HashMap, Hashtable的构造器允许指定一个负载极限,HashSet和HashMap, Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。
70. “负载极限”的默认值(0.75)是时间和空间成本上的一种折中:较高的“负载极限”,可以降低hash表所占用的内存空间,但会增加查询数据的时间销,查询是最频繁的操作(HashMap的get()于put()方法都要用到查询):较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销。程序员可以根据实际况来调整HashSet和HashMap的“负载极限”值。
71. Collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
72.繁琐的Enumeration接口:
(1)boolean hasMoreElements():如果此迭代器还有剩下的元素,则返回true。
(2)Object nextElement( ):返回该迭代器的下一个元素,如果还有的话(否则抛出异常)。
73. 使用Enumeration迭代器来遍历Vector和Hashtable集合里的元素,其工作方式与Iterator迭代器的工作方式基本相似。但使用Enumeration迭代器时方法名更加冗长,而且Enumeration迭代器只能遍历Vector,Hashtable这种古老的集合,因此通常不要使用它。除非在某种极端情况下,不得不使用Enumeration,否则都应该选择Iterator迭代器。

浙公网安备 33010602011771号