集合概述
Java中的集合,又叫容器。它是一个对象。用来存储并管理一组其他对象,存储在集合内的对象称为元素。简单地说,集合对象用来存储、检索、操作和统计一组元素。
在Java SE API中的java.util包设计了一组接口和类,用来实现以不同形式存放对象的存储结构。这样的设计结构被称为Java集合框架(Java Collections Framework)。Java集合框架主要是由两大接口派生而来:一个是Collection接口;另一个是 Map 接口。
![](https://gitee.com/sisyphus2016/media-repository/raw/master/java/01.png)
快速了解
ArrayList
对于ArrayList,它的特点是内部采用动态数组实现,这决定了以下几点。
- 1)可以随机访问,按照索引位置进行访问效率很高,用算法描述中的术语,效率是O(1),简单说就是可以一步到位。
- 2)除非数组已排序,否则按照内容查找元素效率比较低,具体是O(N), N为数组内容长度,也就是说,性能与数组长度成正比。
- 3)添加元素的效率还可以,重新分配和复制数组的开销被平摊了,具体来说,添加N个元素的效率为O(N)。
- 4)插入和删除元素的效率比较低,因为需要移动元素,具体为O(N)。
LinkedList
用法上,LinkedList是一个List,但也实现了Deque接口,可以作为队列、栈和双端队列使用。实现原理上,LinkedList内部是一个双向链表,并维护了长度、头节点和尾节点,这决定了它有如下特点。
- 1)按需分配空间,不需要预先分配很多空间。
- 2)不可以随机访问,按照索引位置访问效率比较低,必须从头或尾顺着链接找,效率为O(N/2)。
- 3)不管列表是否已排序,只要是按照内容查找元素,效率都比较低,必须逐个比较,效率为O(N)
- 。4)在两端添加、删除元素的效率很高,为O(1)。
- 5)在中间插入、删除元素,要先定位,效率比较低,为O(N),但修改本身的效率很高,效率为O(1)。
理解了LinkedList和ArrayList的特点,就能比较容易地进行选择了,如果列表长度未知,添加、删除操作比较多,尤其经常从两端进行操作,而按照索引位置访问相对比较少,则LinkedList是比较理想的选择。
ArrayDeque
ArrayDeque实现了双端队列,内部使用循环数组实现,这决定了它有如下特点。
- 1)在两端添加、删除元素的效率很高,动态扩展需要的内存分配以及数组复制开销可以被平摊,具体来说,添加N个元素的效率为O(N)。
- 2)根据元素内容查找和删除的效率比较低,为O(N)。
- 3)与ArrayList和LinkedList不同,没有索引位置的概念,不能根据索引位置进行操作。
ArrayDeque和LinkedList都实现了Deque接口,应该用哪一个呢?如果只需要Deque接口,从两端进行操作,一般而言,ArrayDeque效率更高一些,应该被优先使用;如果同时需要根据索引位置进行操作,或者经常需要在中间进行插入和删除,则应该选LinkedList。
HashMap
HashMap实现了Map接口,可以方便地按照键存取值,内部使用数组链表和哈希的方式进行实现,这决定了它有如下特点:
- 1)根据键保存和获取值的效率都很高,为O(1),每个单向链表往往只有一个或少数几个节点,根据hash值就可以直接快速定位;
- 2)HashMap中的键值对没有顺序,因为hash值是随机的。
。需要说明的是,HashMap不是线程安全的,Java中还有一个类Hashtable,它是Java最早实现的容器类之一,实现了Map接口,实现原理与HashMap类似,但没有特别的优化,它内部通过synchronized实现了线程安全。
在HashMap中,键和值都可以为null,而在Hashtable中不可以。在不需要并发安全的场景中,推荐使用HashMap。在高并发的场景中,推荐使用ConcurrentHashMap。
TreeMap
TreeMap与HashMap相比,TreeMap同样实现了Map接口,但内部使用红黑树实现。红黑树是统计效率比较高的大致平衡的排序二叉树,这决定了它有如下特点:
- 1)按键有序,TreeMap同样实现了SortedMap和NavigableMap接口,可以方便地根据键的顺序进行查找,如第一个、最后一个、某一范围的键、邻近键等。
- 2)为了按键有序,TreeMap要求键实现Comparable接口或通过构造方法提供一个Com-parator对象。
- 3)根据键保存、查找、删除的效率比较高,为O(h), h为树的高度,在树平衡的情况下,h为log2(N), N为节点数。
HashMap还是TreeMap呢?
不要求排序,优先考虑HashMap,要求排序,考虑TreeMap。
HashSet
HashSet实现了Set接口,内部实现利用了HashMap,有如下特点:
- 1)没有重复元素;
- 2)可以高效地添加、删除元素、判断元素是否存在,效率都为O(1);
- 3)没有顺序。
HashSet可以方便高效地实现去重、集合运算等功能。如果要保持添加的顺序,可以使用HashSet的一个子类LinkedHashSet。Set还有一个重要的实现类TreeSet,它可以排序。
TreeSet
TreeSet实现了Set接口,但有序。在内部实现上,它基于TreeMap实现,而TreeMap基于大致平衡的排序二叉树:红黑树,这决定了它有如下特点。
- 1)没有重复元素。
- 2)添加、删除元素、判断元素是否存在,效率比较高,为O(log2(N)), N为元素个数。
- 3)有序,TreeSet同样实现了SortedSet和NavigatableSet接口,可以方便地根据顺序进行查找和操作,如第一个、最后一个、某一取值范围、某一值的邻近元素等。
- 4)为了有序,TreeSet要求元素实现Comparable接口或通过构造方法提供一个Com-parator对象。
LinkedHashMap
LinkedHashMap可以保持插入顺序或访问顺序。插入顺序经常用于处理键值对的数据,并保持其输入顺序,也经常用于键已经排好序的场景,相比TreeMap效率更高;访问顺序经常用于实现LRU缓存。实现原理上,它是HashMap的子类,但内部有一个双向链表以维护节点的顺序。
LinkedHashSet
之前介绍的Map接口的实现类都有一个对应的Set接口的实现类,比如HashMap有HashSet,TreeMap有TreeSet, LinkedHashMap也不例外,它也有一个对应的Set接口的实现类LinkedHashSet。LinkedHashSet是HashSet的子类,它内部的Map的实现类是LinkedHashMap,所以它也可以保持插入顺序。
PriorityQueue
PriorityQueue实现了队列接口Queue,但按优先级出队,内部是用堆实现的,有如下特点:
- 1)实现了优先级队列,最先出队的总是优先级最高的,即排序中的第一个。
- 2)优先级可以有相同的,内部元素不是完全有序的,如果遍历输出,除了第一个,其他没有特定顺序。
- 3)查看头部元素的效率很高,为O(1),入队、出队效率比较高,为O(log2(N)),构建堆heapify的效率为O(N)。
- 4)根据值查找和删除元素的效率比较低,为O(N)。除了用作基本的优先级队列,PriorityQueue还可以作为一种比较通用的数据结构,用于解决一些其他问题,让我们在下一节继续探讨。