集合框架分析
Java集合包含存储单值的Collection实现类(单列集合,有时直接说“集合”)和存储键值对的Map实现类(双列集合,映射)。
Collection类实现了Collection或其子接口,Map类同理。
AbstractCollection是抽象类,提供了Collection接口的一些实现,以最大限度地减少实现此接口所需的工作量。集合框架内其他抽象类作用类似。
Interface Collection
Collection接口继承了Iterable接口,还规定了以下方法:
方法 | 描述 |
---|---|
add(E e) | 返回boolean,表征是否插入成功 |
addAll(Collection<? extends E> c) | 可将指定集合中的所有元素添加到此集合中(可选) |
clear() | 删除集合里的所有元素(可选) |
contains(Object o) | boolean |
containsAll(Collection<?> c) | |
equals(Object o) | |
hashCode() | |
isEmpty() | |
iterator() | 返回此集合中元素的迭代器, Iterator |
remove(Object o) | boolean,只删除一个实例(可选) |
removeAll(Collection<?> c) | |
retainAll(Collection<?> c) | 仅保留此集合中包含在指定集合中的元素(可选) |
size() | |
toArray() | Object[] |
toArray(T[ ] a) | T[] (指定了运行时类型) |
还包含一些我暂不了解的方法:
方法 | 描述 |
---|---|
parallelStream() | 以此集合为源返回可能并行的流, default Stream |
removeIf(Predicate<? super E> filter) | 删除此集合中满足给定谓词的所有元素 |
spliterator() | 在此集合中的元素上创建Spliterator |
stream() | 返回以此集合为源的顺序流, default Stream |
toArray(IntFunction<T[]> generator) | 返回包含此集合中所有元素的数组,使用提供的generator函数分配返回的数组 |
Collection主要有两个子接口:List(列表)、Set(集)。
List接口
规定可存储重复元素、元素有序的集合。
因为List是有序的,因而其额外规定了一些与索引有关的方法:如按索引查找、修改、添加、删除,返回符合某条件的索引等。
List可以提供一个特殊的迭代器:ListIterator,除了Iterator接口提供的正常操作外,还允许元素插入和替换以及双向访问。还提供了一种方法来获得从列表的指定位置开始的ListIterator。
以下是List增加的额外规定:
方法 | 描述 |
---|---|
add(int index, E element) | 一定会修改列表,所以返回void而不是boolean |
addAll(int index, Collection<? extends E> c) | void |
get(int index) | E |
indexOf(Object o) | 返回此列表中指定元素第一次出现的索引,如果此列表中不包含该元素,则返回-1。 |
lastIndexOf(object o) | int |
listIterator() | 返回此列表中元素的列表迭代器(按适当顺序) |
listIterator(int index) | 从列表的指定位置开始,~ |
of() of(E e1) … of(…E e10) | 返回包含0~10个元素的不可修改列表,static List |
remove(int index) | boolean |
set(int index, E element) | 按索引替换,E(像缓存一样) |
subList(int fromIndex, int toIndex) | [ , ), List |
暂不了解的方法:
方法 | 描述 |
---|---|
copyOf(Collection<? extends E> coll) | 以迭代顺序返回包含给定集合元素的unmodifiable List, static List |
replaceAll(UnaryOperator operator) | 将该列表的每个元素替换为将运算符应用于该元素的结果, default void |
sort(Comparator<? super E> c) | 根据指定的比较算子对此列表进行排列, default void |
List实现类主要有:ArrayList, LinkedList, Vector。(使用比例可能为99%:1%:0)
ArrayList
数组列表,是由数组实现的列表。
默认数组长度为10,充满后还增加新的元素,会自动”扩容“。(动态数组+自动扩容)
因此,其特性为:
- 易查找、修改;
- 增加新元素代价大。
实现接口除List外,还有RandomAccess, Cloneable, Serializable。
注意,ArrayList不同步。
- 如果多个线程同时访问其实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。
- 此类的…方法返回的2种迭代器是快速失败的。
构造方法:
构造器参数 | 描述 |
---|---|
无 | 构造一个初始容量为10的空列表 |
int initialCapacity | |
Collection<? extends E> c | 按照集合的迭代器返回的顺序构造一个包含集合元素的列表 |
新增的方法(指相对于List接口,下同):
方法 | 描述 |
---|---|
clone() | 返回此数组列表实例的浅表副本,Object |
ensureCapacity(int minCapacity) | 如有必要,增加此数组列表实例的容量,以确保它至少可以容纳参数指定的元素数 |
forEach(Consumer<? super E> action) | |
removeRange(int fromIndex, int toIndex) | protected viod |
trimToSize | 将此数组列表实例的容量调整为列表的当前大小,void |
LinkedList
链接列表,由双向链表实现。因此,实现了一些对列表首、尾元素、相反顺序进行操作的方法。
因此,其特性为:
- 增、删快,首、尾都快;
- 查找、修改、遍历慢。
“大量的增加、删除操作是使用链接表的唯一理由”。
实现接口除List外,还有Deque, Cloneable, Seralizable。
注意,LinkedList不同步。
- 如果多个线程同时访问其实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。这通常通过同步自然封装列表的某个对象来完成。如果不存在此类对象,则应使用Collections.synchronizedList方法==“包装”==该列表。这最好在创建时完成,以防意外地不同步访问列表。
- 同ArrayList,返回的迭代器是快速失败的。失败快速迭代器以尽力而为的方式抛出异常,因此,依赖于此异常的程序以确保其正确性是错误的。
允许所有元素,包括null。
构造方法:
构造器参数 | 描述 |
---|---|
无 | 构造一个空列表 |
Collection<? extends E> c |
新增的方法:
方法 | 描述 |
---|---|
addFirst(E e), push(E e) 1.6 | void |
addLast(E e) | 相当于add(E e), void |
clone() | 返回浅表副本,Object |
descendingIterator() | 以相反的顺序返回此双端队列中元素的迭代器, Iterator |
getFirst(), element() 1.5 | E |
getLast() | E |
offer(E e) 1.5 | 将指定的元素添加为此列表的尾部,boolean(由Queue.offer(E)指定为true ?) |
offerFirst(E e) | boolean |
offerLast(E e) | boolean |
peek() 1.5 | 检索但不删除此列表的首部, 如果此列表为空,则返回null, E |
peekFirst() | 检索但不删除此列表的首部,如果此列表为空,则返回null, E |
peekLast() | |
poll() 1.5 | 检索并删除此列表的首部,如果此列表为空,则返回null, E |
pollFirst() | 检索并删除此列表的首部,如果此列表为空,则返回null, E |
pollLast() | |
remove() 1.5 | 检索并删除此列表的首部(空则抛异常), E |
removeFirst(), pop() 1.6 | E |
removeFirstOccurrence(Object o) | 删除此列表第一次出现的指定元素(从头到尾遍历列表时),boolean |
removeLast | E |
removeLastOccurrence(Object o) | boolean |
Vector
向量,由数组实现。通过capacity和capacityIncrement来优化存储管理。
是线程安全的(同步的)。
现很少使用,一般用不同步的ArrayList。
实现的接口与ArrayList相同。
比ArrayList多一个构造方法:
构造器参数 | 描述 |
---|---|
int initialCapacity, int capacityIncrement | 构造具有指定初始容量和容量增量的空间向量 |
新增的方法:
方法 | 描述 |
---|---|
setSize(int newSize) | |
… | … |
Set接口
规定不可存储重复元素、元素无序的集合。
最多1个null元素(某些禁止null作为元素)。
Set实现元素不可重复的原理是借助键值不可重复的Map。
如果将可变对象用作set元素,则必须非常小心。
元素无序指的是“存储元素的时间顺序与元素在数据结构中的位置顺序不一致”。
以下是Set的新增约定:
方法 | 描述 |
---|---|
copyOf(Collection<? extends E> coll) | 返回包含给定Collection的元素的unmodifiable集, static Set |
of() of(E e1)…of(… E e10) of(E… elemnets) | 返回包含0、1、10、任意个元素的不可修改集, static Set |
Set实现类主要有:HashSet和TreeSet。
HashSet
由哈希表(实际上是HashMap实例)支持。允许null元素。
存储结构为动态数组+链接表(桶内元素个数大于8->红黑树)。
查找一个元素,先求其在哪个桶里(对象数组的下标):hashCode()%容量,再在链表或红黑树里调用==equals(o)==方法找。
一旦把对象存入HashSet(或作为键存入HashMap),就不要再修改它。
其存储的对象必须重载hashCode()和equals()方法。
默认初始容量为16,默认加载因子为0.75.
再散列:已用空间 > 容量*负载因子时,将哈希表容量x2,重建哈希表。
- 初始容量太大:浪费空间;太小:频繁散列,浪费时间。
- 加载因子太大:查找慢;太小:浪费空间。
HashSet实现的接口除Set外,还有Cloneable, Serializable
此实现不同步。
-
如果多个线程同时访问其实例,并且至少有一个线程在结构上修改了该集,则必须在外部进行同步。这通常通过同步自然封装集的某个对象来完成。如果不存在此类对象,则应使用Collections.synchronizedSet方法==“包装”==该集。这最好在创建时完成,以防意外地不同步访问。
Set s = Collections.synchronizedSet(new HashSet(...));
-
迭代器:快速失败、尽力而为。
构造方法:
构造器参数 | 描述 |
---|---|
无 | |
int initialCapacity | |
int initialCapacity, float loadFactor | |
Collection<? extends E> c |
部分方法(没什么新鲜的):
方法 | 描述 |
---|---|
add(E e) | 指定的元素不存在时,才会将其添加至此哈希集中,boolean |
clone() | 返回浅表副本 |
contains(Object o) | boolean |
remove(Object o) | boolean |
TreeSet
(一个NavigableSet实现)基于一个TreeMap。采用有序的二叉树存放元素,默认顺序是自然顺序(对象实现Comparable接口的ComparaTo方法)。
add, remove, contains的时间复杂度是O(log(n))。
若存储自定义类型对象,则必须实现下面一个:
- 该类实现java.lang.Comparable接口(实现compareTo(E e)方法,该方法返回int,返回0表征一样大)。
- 创建树集时指定一个Comparator(接口的实现类),作为排序的比较器。
要维护比较器和equals方法的一致性。
- 这是因为Set接口是根据equals操作定义的,但是TreeSet实例使用其compareTo(或compare)方法执行所有元素比较。
树集就是通过比较器实现的元素唯一+有序存放。
实现的接口有NavigableSet, Cloneable, Serializable。
此实现不同步。(细节和HashSet几乎一致,但包装应使用Collections.synchronizedSortedSet)
构造方法:
构造器参数 | 描述 |
---|---|
无 | |
Collection<? extends E> c | |
Comparator<? super E> comparator | 创造一个新的空树集,根据制定的比较器进行排序 |
SortedSet s | 构造一个包含指定有序集相同元素并使用与其相同排序的新树集 |
新增的方法(与Set比较):
方法 | 描述 |
---|---|
clone() | |
first() | 返回第一个元素(最低元素),E |
last() | |
ceiling(E e) | 返回此集中≥给定元素的最小元素,如果没有则返回null, E |
floor(E e) | |
higher(E e) | 返回此集中>给定元素的最小元素,如果没有则返回null, E |
lower(E e) | |
pollFirst() | 检索并删除第一个(最低)元素,集为空则返回null, E |
descendingIterator() | 以降序返回迭代器, Iterator |
暂不了解的方法:
方法 | 描述 |
---|---|
descendingSet() | 返回此集中包含的元素的逆序视图, NavigableSet |
headSet(E toElement) | 返回此集的部分视图,其元素严格小于toElement, SortedSet |
headSet(E toElement, boolean inclusive) | 返回此集的部分视图,其元素小于(或等于,如果inclusive为true)toElement, NavigableSet |
tailSet(E fromElement) | |
tailSet(E from Element, bollean inclusive) | |
subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) | NavigableSet |
subSet(E fromElement, E toElement) | SortedSet |
Interface Map<K,V>
该接口规定的方法:
方法 | 描述 |
---|---|
clear() equals(Object o) hashCode() isEmpty() size() | |
containsKey(Object key) | boolean |
containsValue(Object value) | boolean |
copyOf(Map<? extends K, ? extends V> map) | static <K,V> Map<K,V> |
forEach(BiConsumer<? super K, ? super V> action) | |
get(Object key) | V |
getOrDefault(Object key, V defaultValue) | 返回映射到的值,如果此映射不包含键的映射则返回defaultValue,default V |
keySet() | Set |
values() | Collection |
of() of(K k1, V v1)…of(…K k10, V v10) | static <K,V> Map<K,V> |
put(K key, V value) | V |
putAll(Map<? extends K, ? extends V> m) | void |
putIfAbsent(K key, V value) | 关联成功返回null,否则返回当前值, dafault V |
remove(Object key) | V |
remove(Object key, Object value) | 仅当指定键当前映射到指定值时才删除,default boolean |
replace(K key, V value) | default V |
replace(K key, V oldValue, V newValue) | dafault boolean |
replaceAll(BiFunction<? super K, ? super V, ? extends V> function) | 将每个条目的值替换为在该条目上调用给定函数的结果,default void |
暂不了解的方法:
方法 | 描述 |
---|---|
compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) | 计算当前指定键及其当前映射值的映射, default V |
merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) | 如果指定的键尚未与值关联(或映射到null),则将其与给定的非空值关联default V |
computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) | 如果指定的键尚未与值关联(或映射到null),则使用给定的映射函数计算其值并将其输入此映射, default V |
computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> mappingFunction) | 如果指定的键的值存在且不为null,则在给定键及其当前映射值的情况下计算其新映射,default V |
entry(K k, V v) | static <K,V> Map.Entry<K,V> |
entrySet() | Set<Map.Entry<K,V>> |
ofEntries(Map.Entry<? extends K, ? extends V>… entries) | 返回包含从给定条目中提取的键和值的不可修改映射, static <K,V> Map<K,V> |
Map的实现类主要有HashMap, WeakHashMap, Hashtable, TreeMap。
HashMap
实现接口Map<K,V>, Cloneable, Serializable。
基于哈希表实现。允许null值和null键。
HashMap大致相当于Hashtable,除了它是不同步的并且允许空值。
两个参数:初始容量和负载因子。(默认16、0.75)
详见HashSet。
HashMap插入键值对的源码分析:
当键为实现Comparable接口的对象时,可以使用键之间的比较顺序来改善多键hashCode()相同带来的性能损耗。
此实现不同步。
- ……Collections.synchronizedMap……
- 迭代器快速失败、尽力而为。
构造方法:
构造器参数 | 描述 |
---|---|
无 | |
int initialCapacity | |
int initialCapacity, float loadFactor | |
Map<? extends K, ? extends V> m |
TreeMap
实现接口NavigableMap<K,V>, Cloneable, Serializable。
……详见TreeSet。
构造方法:
构造器参数 | 描述 |
---|---|
无 | |
Comparator<? super K> comparator | |
Map<? extends K, ? extends V> m | |
SortedMap<K, ? extends V> m |
新增方法(与Map比):
方法 | 描述 |
---|---|
descendingKeySet() | NavigableSet |
descendingMap() | NavigableMap<K,V> |
ceilingEntry(K key) | Map.Entry<K,V> |
floorEntry(K key) | |
higherEntry(K key) | Map.Entry<K,V> |
lowerEntry(K key) | |
firstEntry() | Map.Entry<K,V> |
lastEntry() | |
pollLastEntry() | Map.Entry<K,V> |
ceilingKey(K key) | K |
floorKey(K key) | |
higherKey(K key) | K |
lowerKey(K key) | |
firstKey() | K |
lastKey() | |
headMap(K tokey) | SortedMap<K,V> |
tailMap(K fromKey) | |
headMap(K toKey, boolean inclusive) | NavigablrMap<K,V> |
tailMap(K fromKey, boolean inclusive) | |
subMap(K fromKey, K toKey) | SortedMap<K,V> |
subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) | NavigableMap<K,V> |
时间顺序
JDK1.2出现Java Collections Framework, 其中Vector是由1.0版的Vector改进而来以实现List接口。部分集合也取代了之前的一些类。
Java5新增Collection第三个子接口:Queue(队列)。
更多框架图
每张图都不是最完善的,(可能是不重要的被省略了,也可能是版本更迭了),需要综合起来看。
放大局部:
综合点评
ArrayList是最常用的集合。允许null值。擅长随机访问。
LinkedList插入删除元素性能较好,还实现了Deque接口,可以当双端队列(含栈,队列)使用。
Vector与ArrayList类似,但是是同步的。
Stack是Vector的子类。
(尽量避免在关键属性将来有可能被修改的情况下使用set和 map)
HashSet是Set最常用的实现类,是其经典实现。集合元素值可以为null。
LinkedHashSet是HashSet的子类,由于其需要维护元素的插入顺序(链接表+哈希表),因此性能略低于HashSet,但在遍历时有很好的性能。
TreeSet是SortedSet接口的实现类,保证元素处于排序状态。
EnumSet是一个专为枚举类(元素只能是同一个枚举类的枚举值)设计的集合类,不允许添加null值。元素也是有序的(枚举值在Enum类内的定义顺序)。
综合性能:EnumSet > HashSet > LinkedHashSet > TreeSet
HashMap\LinkedHashMap\TreeMap\EnumMap的比较类似Set。
(HashMap与Hashtable的关系完全类似于ArrayList和Vector)
所有集合类都位于java.util包下,但支持多线程的集合类位于java.util.concurrent包下。(比如java.util.Hashtable(独占锁)虽然也是线程安全的,但java.util.concurrent.ConcurrentHahMap(分段锁)效率更高)
Iterator接口和ListIterator接口
Iterator是一个接口,它是集合的迭代器。集合可以通过Iterator去遍历集合中的元素。Iterator提供的API接口如下:
♦ boolean hasNext():判断集合里是否存在下一个元素。如果有,hasNext()方法返回 true。
♦ Object next():返回集合里下一个元素。
♦ void remove():删除集合里上一次next方法返回的元素。
ListIterator接口继承Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口的基础上增加了以下几个方法:
♦ boolean hasPrevious():判断集合里是否存在上一个元素。如果有,该方法返回 true。
♦ Object previous():返回集合里上一个元素。
♦ void add(Object o):在指定位置插入一个元素。
以上两个接口相比较,不难发现,ListIterator增加了向前迭代的功能(Iterator只能向后迭代),ListIterator还可以通过add()方法向List集合中添加元素(Iterator只能删除元素)。
Collections
集合工具类:提供搜索、排序、视图等面向集合的静态方法。
如,排序:static void sort(List list, Comparator<? super T> c)