目录
- Java 集合类的基本概念
- Java 集合类的层次关系
- Java 集合类的应用场景
一. Java集合类的基本概念
在编程中,常需要集中存放多个数据,数组是一个很好的选择,但数组的长度需提前指定且不可变,如果我们需要保存一个动态增长的数据(其数量不确定),Java集合类可以很好实现。
集合类又称为容器类。所有的集合类都位于 java.util 包下,为了处理多线程环境下的并发安全问题,在 java.util.concurrent 包下提供了一些多线程支持的集合类。
Java集合类可分为两大类:
1)Collection :
- 1.1)List必须保持元素特定的顺序
- 1.2)Set不能有重复元素
- 1.3)Queue保持一个队列(先进先出)的顺序
2)Map :
- 保存的是一组 “键值对” 对象 (key-value对)
【虚线箭头表示实现关系,实线箭头表示继承关系】
二. Java集合类的层次关系
1. Iterable<T> 接口
java.lang 下的 Interface Iterable<T> 迭代器接口,是Collecton接口的父接口,包括
- default void forEach() 方法
- Iterator<T> iterator() 方法 【Iterator<T> 是java.util 下的接口】
实现这个Iterable接口的对象允许使用foreach进行遍历,所以,所有的Collection集合对象都具有"foreach可遍历性"。
2. Collection 接口
1)Set 接口,继承自Collection 接口。“丢进”Set集合里的多个对象之间没有明显的顺序,不能包含重复元素。【元素特点:无序,不重复】
注:Set判断两个元素是否相同是用equals(object)方法,相同返回true,不同返回false,Set会接受该新元素,而不是用“==”运算符。因此,Set的实现类应实现一个有效的equals(object)方法;
- HashSet :允许使用null元素,基于HashMap实现;【非线程安全】
HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。
注:HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法的返回值相等
- LinkedHashSet:继承自HashSet,允许null元素,LinkedHashSet中有一个LinkedHahsMap(适配器模式),基于双向链表和HashMap实现;【非线程安全】
LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不同的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)
- TreeSet : 不允许null元素,基于TreeMap 实现,底层是红黑树;【非线程安全】
Treeset有两种排序方式:自然排序和比较器排序。
A)自然排序:默认排序方式,java.lang 下的comparable 接口,实现了int compareTo(T o)方法;
obj1.compareTo(obj2)方法如果返回0,表示两个对象相等;如果返回一个正数,则表明obj1>obj2;如果返回一个负数,则表明obj1<obj2.
如果两个对象的equals方法返回true,则其compareTo方法应该返回0;
B)比较器排序:java.util 下的comparator接口,实现了int compare(T o1,T o2)方法,取决于创建TreeSet时所用的构造方法;
TreeSet实现java.io.Serializable的方式。当写入到输出流时,依次写入“比较器、容量、全部元素”;当读出输入流时,再依次读取。
2)List接口,List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许加入重复元素,因为它可以通过索引来访问指定位置的集合元素。【元素特点:有序,可重复】
- ArrayList : 允许null 元素,基于数组实现,它封装了一个动态的增长的、允许再分配的Object[]数组。【非线程安全】
- LinkedList : 允许null元素,基于双端链表实现,可用作队列和栈;Deque接口继承自Queue接口。【非线程安全】
- Stack,继承自Vector,用于模拟“栈”先进后出;【线程安全】
3)Queue接口
用于模拟"队列"这种数据结构(先进先出 FIFO)。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问队列中的元素。
- PriorityQueue : 不允许null元素,按照队列元素的大小进行重新排序,底层以二叉堆实现;
- ArrayDeque : 是一个基于数组的双端队列,和ArrayList类似,它们的底层都采用一个动态的、可重分配的Object[]数组来存储集合元素
- LinkedList
3. Map 接口
Map用于保存具有"映射关系"的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value。key和value都可以是任何引用类型的数据。 Map 的key不能重复,允许null值,value可重复,允许null值;
- HashMap : jdk1.7与jdk1.8的底层实现原理有差别,jdk1.7 :散列表+链表 ;jdk1.8: 散列表+链表+红黑树 【非线程安全】
两个key通过equals()方法比较返回true,同时两个key的hashCode值也必须相等。
- LinkedHashMap : 继承自HashMap, 底层是双向链表+HashMap 【非线程安全】
- TreeMap :实现了SortedMap<K,V>接口,该接口继承自Map接口,底层实现是红黑树【非线程安全】
TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。同样,TreeMap也有两种排序方式: 自然排序、定制排序。
- Hashtable :【线程安全】
三、集合类的使用场景
1.Set集合类的应用场景(HashSet、LinkedHashSet、TreeSet)
1)HashSet的性能总是比TreeSet好,因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
2)对于普通的插入、删除操作,LinkedHashSet比HashSet要略慢一点,这是由维护链表所带来的开销造成的。不过,因为有了链表的存在,遍历LinkedHashSet会更快。
3)EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
4)HashSet、TreeSet、EnumSet都是"线程不安全"的,通常可以通过Collections工具类的synchronizedSortedSet方法来"包装"该Set集合。
Set s = Collections.synchronizedSet(new HashSet<>());
或SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...)); 2.List 集合类的应用场景(ArrayList 、LinkedList) 1)java提供的List就是一个"线性表接口",ArrayList(基于数组的线性表)、LinkedList(基于链表的线性表)是线性表的两种典型实现。 2)Queue代表了队列,Deque代表了双端队列(既可以作为队列使用、也可以作为栈使用) 3)内部以链表作为底层实现的集合在执行插入、删除操作时有很好的性能 4)因为数组以一块连续内存来保存所有的数组元素,所以数组在随机访问时性能最好。所以的内部以数组作为底层实现的集合在随机访问时性能最好。
3.Map集合类的应用场景(HashMap 、 linkedHashMap、TreeMap)
1)HashMap和Hashtable的效率大致相同,因为它们的实现机制几乎完全一样。但HashMap通常比Hashtable要快一点,因为Hashtable需要额外的线程同步控制。2) TreeMap通常比HashMap、Hashtable要慢(尤其是在插入、删除key-value对时更慢),因为TreeMap底层采用红黑树来管理key-value对。3) 使用TreeMap的一个好处就是: TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作。