文章目录
概述
Java的集合类主要有两个接口派生而出:
- Collection
- Map
Collection和Map是java集合框架的根接口,这两个接口又包含了一些子接口或实现类
上图显示了Collection体系里的集合。Set和List接口是Collection接口派生的两个子接口,分别代表了无序集合和有序集合;Queue是Java提供的队列实现,类似于List。
上图显示的是Map体系的里的集合,Map接口的众多实现类在功能、用法上存在一定差异,但都是key-value对的形式保存。
Set集合
Set集合类似于一个罐子,对象被丢进Set集合中后,Set集合通常不能记住元素的添加顺序
Set集合不允许包含相同的元素
Set的3个实现类都是线程不安全的
HashSet类
HashSet是Set接口的典型表现,大多数使用Set集合时可以使用这个实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能
- HashSet不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化
- HashSet不是同步的,有多个线程同时访问一个HashSet,有两个或以上线程修改了HashSet时,必须通过代码来保证其同步。
- HashSet集合元素可以是null
【注意】 把一个对象放入HashSet中时,如果重写该对象的equals()方法,也应重写hashCode()方法,如果equals()方法返回true,两个对象的hashCode值也应该相同。
LinkedHashSet类
- LinkedHashSet是HashSet的一个子类,LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表来维护元素的次序,这样使得元素看起来是以插入的顺序来保存的,即当遍历LinkedHashSet中的元素时,将会按照元素的添加顺序来访问。
- LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet,但由于是以链表维护内部顺序,迭代访问时性能较好
TreeSet类
TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
- TreeSet并不是以元素的插入顺序进行排序的,而是根据元素的实际值的大小进行排序的。TreeSet采用红黑树的数据结构来存储集合元素
EnumSet类
- EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。
- EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序
- EnumSet集合中不允许加入null值。
【比较】
HashSet的性能比TreeSet好,但需要排序时,使用TreeSet
LinkedHashSet在插入、删除操作时,比HashSet慢一点,但有了链表,遍历更快
HashSet、TreeSet、EnumSet都是线程不安全的
List集合
List集合代表一个有序、可重复的集合,集合中的每个元素多有其对应的顺序索引
List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素
ArrayList和Vector类
ArrayList和Vector是List的两个典型实现,完全支持List接口的全部功能
ArrayList和Vector类都是基于数组实现的List类,所以这两类都封装了一个动态的,允许再分配的Object[]数组
- ArrayList和Vector在用法上几乎完全相同,但Vector是一个古老的集合(JDK1.0就有了),Vector里提供了一些方法名很长的方法。
- ArrayList和Vector的显著区别是:
- ArrayList是线程不安全的,程序要手动保证该集合的同步性;
- Vector集合是线程安全的,所以性能要比ArrayList要低
- Vector还提供一个Stack子类,用于模拟“栈”这种数据结构。由于Stack继承了Vector,同样是线程安全的、性能较差的。
Queue集合
Queue集合用于模拟队列这种数据结构,队列通常是“先进先出”(FIFO)的容器。
Queue接口有一个PriorityQueue实现类,还有一个Deque接口
Deque代表一个“双端队列”,双端队列可以同时从两端来添加、删除元素,因此Deque的实现类即可当成队列使用,也可当成栈使用。
PriorityQueue实现类
PriorityQueue是一个比较标准的队列实现类。PriorityQueue保存队列元素的顺序不是按加入队列的顺序,而是按队列元素大小重新排序,从这个意义上看,其实PriorityQueue已经违反了队列最基本的先进先出(FIFO)的原则。
- PriorityQueue不允许插入null元素
Deque接口和ArrayDeque实现类
Deque接口是Queue接口的子接口,它代表一个双端队列,Deque接口里定义了一些双端队列的方法,这些方法允许从两端来操作队列的元素。其中,Deque包含了pop(出栈)和push(入栈)两个方法,因此可以当成栈来使用。
Deque接口提供了一个典型的实现类:ArrayDeque,它是基于数组实现的双向队列。
- ArrayDeque和ArrayList的实现机制基本相似,它们的底层都采用一个动态的,可重分配到Object[]数组来存储集合元素,当集合元素超出该数组的容量时,系统会在底层重新分配一个数组来存储
- ArrayDeque类作为Deque的实现类,不仅可以作为詹世勇,也可以作为队列使用
LinkedList实现类
LinkedList可是List接口的实现类,可以根据索引来随机访问集合中的元素,还是先了Deque接口,可以被当成双端队列来使用。
- LinkedList与ArrayList、ArrayDeque的实现机制完全不同
- ArrayList、ArrayDeque内部以数组的形式来保存集合中的元素,因此随机集合元素时有较好的性能
- LinkedList内部已链表的形式来保存集合中的元素,因此随机访问集合元素是性能较差,但在插入、删除元素时性能比较出色(只需改变指针所指的地址即可)
线性表的性能分析
ArrayList和LinkedList是线性表的两种典型实现:基于数组的和基于链的,Queue代表了队列,Deque代表了双端队列
一般来说,由于数组以一块连续内存区来保存所有的数组元素,所以数组在随机访问时性能最好,即内部已数组作为底层实现的集合在随机访问时性能都比较好;而内部已链表作为底层实现的集合在执行插入、删除操作时有较好的性能。
- 如果需要遍历List集合元素,对于ArrayList、Vector集合,应该使用随机访问方法(get)来遍历集合元素;对于LinkedList集合,则应该使用迭代器(Iterator)来遍历集合元素
- 如果进程执行插入、删除操作,考虑使用LinkedList集合
- 如果有多个线程同时访问List集合中的元素,可以考虑使用Collections将集合包装成线程安全的集合。
Map集合
Map用于保存具有映射关系的数据,即Map里保存着两组值,key和value,Map的key不允许重复
key和value之间存在单向一对一关系,通过key总能找到唯一的、确定的value
如果把Map里的key放在一起,就组成了一个Set集合
Map中包括了一个内部类Entry,该类封装了一个key-value对。
HashMap和Hashtable类
HashMap和Hashtable都是Map接口的典型实现类,Hashtable是一个古老的实现类,从JDK1.0起就出现了。
HashMap视为快速查询设计的(其底层其实也是采用数组来存储键值对)
- Hashtable是一个线程安全的Map实现,HashMap是线程不安全的实现,自然,HashMap的性能要高一些。
- Hashtable不允许使用null作为key和value,而HashMap可以。
LinkedHashMap实现类
LinkedHashMap使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。
- LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap;但因为以链表来维护内部顺序,在迭代访问Map中的元素是有较好的性能。
SortedMap接口和TreeMap实现类
和Set接口派生出SortedSet子接口,Sorted接口有一个TreeSet实现类类似,Map下面也有一个SortedMap子接口,下面也有一个TreeMap实现类。
- TreeMap就是一个红黑树数据结构,存储键值对是,需要根据key对接点进行排序
WeakHashMap实现类
WeakHashMap与HashMao的用法基本类似,但区别在于:
- HashMap的key保留了对实际对象的强引用,只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收
- WeakHashMap的key只保留了对实际对象的弱引用,如果key所引用的对象没有被其他强引用变量所应用,这些key所引用的对象可能被垃圾回收
操作集合的工具类:Collections
Collections工具类提供了大量方法对集合元素进行排序、查询和修改等操作。还提供了多个synchronizedXxx()方法,将指定几个包装成线程同步的集合,从而可以结局多线程并发访问集合时的线程安全问题。