集合概述
Java集合概览
集合由两个接口派生而来,Collection:存放单一元素;Map:存放键值对。Collection下三个子接口:List、Set、Queue
List、Set、Queue、Map
-
List:
有序
,按对象进入顺序
保存对象,可重复
,允许多个null元素对象,可以使用Iterator取出所有元素逐一遍历,还可以使用get(int index)获取指定下表的对象- ArrayList:ArrayList是List的主要实现类,底层使用
Object[]
存储,适用于频繁的查找工作,线程不安全 - Vector:是List的古老实现类,底层使用
Object[]
存储,线程安全。 - LinkedList:使用双向链表数据结构(1.6前为循环链表,1.7取消了循环)
- ArrayList:ArrayList是List的主要实现类,底层使用
-
Set:
无序
,不可重复
,最多允许一个null元素对象,只能用Iterator接口取得所有元素,再逐一遍历- HashSet:基于HashMap实现,支持快速查找,不支持有序性操作。并且失去了元素的插入顺序信息,即使用
Iterator
遍历HashSet
得到的结果不确定。 - LinkedHashSet:LinkedHashSet具有
HashSet
的查找效率,内部使用双向链表维护元素插入顺序(FIFO)。 - TreeSet:基于红黑树实现,支持有序性操作,如根据一个范围查找元素的操作。效率不如
HashSet
,HashSet
时间复杂度为O(1),TreeSet
为O(logN)
- HashSet:基于HashMap实现,支持快速查找,不支持有序性操作。并且失去了元素的插入顺序信息,即使用
-
Queue:
先后顺序
,有序
,可重复
- PriorityQueue:
Object[]
实现二叉堆 - ArrayQueue:
Object[]
+双指针
- PriorityQueue:
-
Map :键值对存储,key
无序
、不可重复
,value无序
、可重复
。每个键最多一个值HashMap
: JDK1.8 之前HashMap
由数组+链表组成的,数组是HashMap
的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间LinkedHashMap
:LinkedHashMap
继承自HashMap
,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap
在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:《LinkedHashMap 源码详细分析(JDK1.8)》 (opens new window)Hashtable
: 数组+链表组成的,数组是Hashtable
的主体,链表则是主要为了解决哈希冲突而存在的TreeMap
: 红黑树(自平衡的排序二叉树)
如何选用集合
主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用 Map
接口下的集合,需要排序时选择 TreeMap
,不需要排序时就选择 HashMap
,需要保证线程安全就选用 ConcurrentHashMap
。
当我们只需要存放元素值时,就选择实现Collection
接口的集合,需要保证元素唯一时选择实现 Set
接口的集合比如 TreeSet
或 HashSet
,不需要就选择实现 List
接口的比如 ArrayList
或 LinkedList
,然后再根据实现这些接口的集合的特点来选用。
为什么要使用集合
使用数组存储对象有一定的弊端。数组一旦声明长度不可变、类型不可变、有序、可重复,特点单一。集合提高了数据存储的灵活性,还可以保存具有映射关系的数据。
List
ArrayList和LinkedList的区别
- **是否保证线程安全:**A和L都是不同步的,不保证线程安全
- **底层数据结构:**A使用Object[]数组,L使用双向链表数据结构(1.6前为循环链表,1.7取消了循环)
- **插入和删除是否受元素位置影响:**A采用数组存储,受元素位置影响,第i和后面n-i个元素都要向前/后移,o(n-i);L需要移动到指定位置,再执行插入,o(n)
- **是否支持快速随机访问:**L不支持,A支持。
- **空间占用:**A体现在list列表结尾会预留一定的容量空间。L体现再每一个元素要消耗比A更多的空间(要存放直接后继、前驱以及数据)
Set
比较TreeSet、HashSet、LinkedHashSet的异同?
- 三者都是Set接口的实现类,都能保证元素唯一,并且都不是线程安全的
- 主要区别在于底层数据结构不同
- 应用场景不同,HashSet不需要保证元素插入和取出顺序的场景,LinkedHashSet用于保证元素插入和取出顺序的场景,TreeSet用于支持对元素自定义排序规则的场景。
Queue
Queue和Deque的区别
- Queue
- Q单端队列,只能从一段插入元素,另一端删除元素,遵循FIFO
- Q扩展了Collection接口,根据因为容量问题导致操作失败后处理方式不同分为两类方法:
Queue
接口抛出异常 返回特殊值 插入队尾 add(E e) offer(E e) 删除队首 remove() poll() 查询队首元素 element() peek()
- Deque
- D是双端队列,在队列两端均可插入/删除元素
- 扩展了Queue的接口,增加了在队首队尾插入删除的操方法:
Deque 接口 | 抛出异常 | 返回特殊值 |
---|---|---|
插入队首 | addFirst(E e) | offerFirst(E e) |
插入队尾 | addLast(E e) | offerLast(E e) |
删除队首 | removeFirst() | pollFirst() |
删除队尾 | removeLast() | pollLast() |
查询队首元素 | getFirst() | peekFirst() |
查询队尾元素 | getLast() | peekLast() |
ArrayDeque和LinkedList的区别
都实现了Deque接口,都具有队列功能
- A是基于可变长的数组和双指针来实现,L通过链表实现
- A不支持存储NULL数据,L支持
- A在JDK1.6后才引入,L在1.2就存在
- A插入可能存在扩容过程,均摊后插入操作依然为O(1),L不存在扩容,但每次插入需要申请新的堆空间,均摊性能相比更慢
- A实现队列比L更好,A也可以用来实现栈
PriorityQueue
1.5中被引入,与Queue区别在于元素出队顺序与优先级有关
- PriorityQueue 利用二叉堆数据结构来实现,底层使用变长的数据存储数据
- PQ通过堆元素上浮下沉,实现logn时间复杂度内插入删除
- PQ非线程安全,不支持存储
NULL
和no-comparable
的对象 - PQ默认小顶堆,但可以接收一个
Comparator
作为构造参数,从而定义元素优先级先后。
Map
HashMap和HashTable的区别
- **线程是否安全:**M非线程安全,T线程安全。T内部方法基本都是经过synchronized修饰。
- **效率:**H更高,T基本被淘汰
- **对Null key和Null value的支持:**M可以存储null的key和value,但null作为键只能有一个,null作为值可以有很多个;T不允许
- 初始容量大小和每次扩容量大小的不同:
- **不指定容量初始值:**T默认11,每次扩容2n+1。M默认16,每次扩容,容量变为原来的两倍
- **指定容量初始值:**T使用给定大小,M会将其扩充为2的幂次方大小(
HashMap
中的tableSizeFor()
方法保证)。即M总会使用2的幂作为哈希表大小
- **底层数据结构:**1.8以后M解决哈希冲突上有了较大的变化,当链表长度大于阈值(默认8)(将链表转换为红黑树前会判断,如果当前数组长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树),将链表转换为红黑树,以减少搜索时间。T没有
HashMap
中带有初始容量的构造函数:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
HashMap和HashSet的区别
看过 HashSet
源码的话就应该知道:HashSet
底层就是基于 HashMap
实现的。(HashSet
的源码非常非常少,因为除了 clone()
、writeObject()
、readObject()
是 HashSet
自己不得不实现之外,其他方法都是直接调用 HashMap
中的方法。
HashMap | HashSet |
---|---|
实现了 Map 接口 | 实现 Set 接口 |
存储键值对 | 仅存储对象 |
调用 put() 向 map 中添加元素 | 调用 add() 方法向 Set 中添加元素 |
HashMap 使用键(Key)计算 hashcode | HashSet 使用成员对象来计算hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals() 方法用来判断对象的相等性 |
HashMap和TreeMap的区别
都继承自AbstractMap
,T还实现了NavigableMap
接口和SortedMap
接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8Jdmcdp-1646738917817)(data:image/png;base64,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)]
NavigableMap让T有了对集合内元素搜索的能力
实现SortedMap接口让TreeMap有了对集合中的元素根据键排序的能力。默认key升序排序(也可以指定排序的比较器)
/**
* @author shuang.kou
* @createTime 2020年06月15日 17:02:00
*/
public class Person {
private Integer age;
public Person(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public static void main(String[] args) {
TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person person1, Person person2) {
int num = person1.getAge() - person2.getAge();
return Integer.compare(num, 0);
}
});
treeMap.put(new Person(3), "person1");
treeMap.put(new Person(18), "person2");
treeMap.put(new Person(35), "person3");
treeMap.put(new Person(16), "person4");
treeMap.entrySet().stream().forEach(personStringEntry -> {
System.out.println(personStringEntry.getValue());
});
}
}
综上,相比于HashMap
来说 TreeMap
主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。
HashSet检查重复
当你把对象加入HashSet
时,HashSet
会先计算对象的hashcode
值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode
值作比较,如果没有相符的 hashcode
,HashSet
会假设对象没有重复出现。但是如果发现有相同 hashcode
值的对象,这时会调用equals()
方法来检查 hashcode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让加入操作成功。
jdk8中,HashSet的add()方法只是简单调用了HashMap的put()方法,并且判断了一下返回值确保是否有重复元素。
// Returns: true if this set did not already contain the specified element
// 返回值:当set中没有包含add的元素时返回真
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//HashMap的putVal()方法中也有说明
// Returns : previous value, or null if none
// 返回值:如果插入位置没有元素返回null,否则返回上一个元素
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
...
}
所以,无论HashSet中是否已经存在某元素,HashSet都会直接插入,只是会在add()方法返回值处告诉我们插入前是否存在相同元素。
HashMap底层实现
- 结构:数组+链表,链表节点存储的是一个Entry对象,每个Entry对象存储四个属性(hash,key,value,next)
- 初始化HashMap,有参和无参。无参中,初始容量为16,加载因子为0.75,阈值为16*0.75=12;
JDK1.7:
-
根据对冲突处理方式不同,哈希表两种实现方式:1、开放地址方式;2、冲突链表方式。java7采用冲突链表方式。
有两个参数影响HashMap的性能:初始容量(inital capacity)和负载系数(load factor)。
初始容量:指定初始table的大小
负载系数:指定自动扩容的临界值
当entry数量超过capacity*load_factor时,容器自动扩容并重新哈希。对于插入元素较多的场景,将初始容量设大可以减少重新哈希次数。
将对象放入到HashMap或HashSet中时,有两个方法需要特别关心: hashCode()
和equals()
。hashCode()
方法决定了对象会被放到哪个bucket
里,当多个对象的哈希值冲突时,equals()
方法决定了这些对象是否是“同一个对象”。所以,如果要将自定义的对象放入到HashMap
或HashSet
中,需要*@Override*hashCode()
和equals()
方法。
-
get()
get(Object key)方法根据指定key返回对应的value,该方法调用了getEntry(Object key)得到相应的entry,然后返回entry.getValue()。因此,
getEntry()
是算法的核心。算法思想是:首先通过Hash()函数得到对应bucket下标,然后依次遍历冲突链表,通过key.equals(k)
方法来判断是否是要找的那个entry
。
上图中hash(k)&(table.length-1)
等价于hash(k)%table.length
,原因是HashMap要求table.length
必须是2的指数,因此table.length-1
就是二进制低位全是1,跟hash(k)
相与会将哈希值的高位全抹掉,剩下的就是余数了。
//getEntry()方法
final Entry<K,V> getEntry(Object key) {
......
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[hash&(table.length-1)];//得到冲突链表
e != null; e = e.next) {//依次遍历冲突链表中的每个entry
Object k;
//依据equals()方法判断是否相等
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
-
put()
put(K key, V value)
方法是将指定的key, value
对添加到map
里。该方法首先会对map
做一次查找,看是否包含该元组,如果已经包含则直接返回,查找过程类似于getEntry()
方法;如果没有找到,则会通过addEntry(int hash, K key, V value, int bucketIndex)
方法插入新的entry
,插入方式为头插法。- 先通过HM自己提供的hash算法算出当前key的hash值
- 通过计算出的hash值调用indexFor方法计算当前对象应该存储在数组几号位置
- 判断size是否达到阈值,没有继续,达到,数组扩容为两倍(size是当前容器中已有Entry的数量,不是数组长度。)
- 将当前对应的hash,key,value封装成一个Entry,去数组中查找当前位置有无元素。没有放在这个位置;如果存在链表,遍历链表,若链表上某节点key与当前key进行equals后比较结果为true,将原来节点的value返回,将当前新的value替换为原来的value,若比较结果没有true,把刚才的Entry中next指向当前链表始节点,即第一个位置
扩容机制:HM使用懒扩容,只会在put时才判断。注意,每次扩容后,都要重新计算原来的Entry在新数组中的位置。
//addEntry() void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length);//自动扩容,并重新哈希 hash = (null != key) ? hash(key) : 0; bucketIndex = hash & (table.length-1);//hash%table.length } //在冲突链表头部插入新的entry Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; }
-
remove()
//removeEntryForKey()
final Entry<K,V> removeEntryForKey(Object key) {
......
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);//hash&(table.length-1)
Entry<K,V> prev = table[i];//得到冲突链表
Entry<K,V> e = prev;
while (e != null) {//遍历冲突链表
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {//找到要删除的entry
modCount++; size--;
if (prev == e) table[i] = next;//删除的是冲突链表的第一个entry
else prev.next = next;
return e;
}
prev = e; e = next;
}
return e;
}
JDK1.8
数组+ 链表+ 红黑树
java7中查找时根据hash值找到数组下标,但后面要顺着链表一个个比较下去才能找到,时间复杂度取决于链表长度,为O(n)。
为了降低这部分开销,java8中,当链表元素达到8个,会将链表转换为红黑树,在这里进行查找时间复杂度降为O(logN)。
java8使用Node代替entry,但只用于链表的情况,红黑树的情况使用TreeNode
-
put()
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } // 第四个参数 onlyIfAbsent 如果是 true,那么只有在不存在该 key 时才会进行 put 操作 // 第五个参数 evict 我们这里不关心 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; // 第一次 put 值的时候,会触发下面的 resize(),类似 java7 的第一次 put 也要初始化数组长度 // 第一次 resize 和后续的扩容有些不一样,因为这次是数组从 null 初始化到默认的 16 或自定义的初始容量 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 找到具体的数组下标,如果此位置没有值,那么直接初始化一下 Node 并放置在这个位置就可以了 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else {// 数组该位置有数据 Node<K,V> e; K k; // 首先,判断该位置的第一个数据和我们要插入的数据,key 是不是"相等",如果是,取出这个节点 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 如果该节点是代表红黑树的节点,调用红黑树的插值方法,本文不展开说红黑树 else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // 到这里,说明数组该位置上是一个链表 for (int binCount = 0; ; ++binCount) { // 插入到链表的最后面(Java7 是插入到链表的最前面) if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // TREEIFY_THRESHOLD 为 8,所以,如果新插入的值是链表中的第 8 个 // 会触发下面的 treeifyBin,也就是将链表转换为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } // 如果在该链表中找到了"相等"的 key(== 或 equals) if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) // 此时 break,那么 e 为链表中[与要插入的新值的 key "相等"]的 node break; p = e; } } // e!=null 说明存在旧值的key与要插入的key"相等" // 对于我们分析的put操作,下面这个 if 其实就是进行 "值覆盖",然后返回旧值 if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; // 如果 HashMap 由于新插入这个值导致 size 已经超过了阈值,需要进行扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
相比起来,JDK1.7性能稍差,因为扰动了4次。
- **拉链法:**将链表和数组相结合。
HashMap遍历方式
-
迭代器EntrySet
public class HashMapTest { public static void main(String[] args) { // 创建并赋值 HashMap Map<Integer, String> map = new HashMap(); map.put(1, "Java"); map.put(2, "JDK"); map.put(3, "Spring Framework"); map.put(4, "MyBatis framework"); map.put(5, "Java中文社群"); // 遍历 Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, String> entry = iterator.next(); System.out.println(entry.getKey()); System.out.println(entry.getValue()); } } }
1 Java 2 JDK 3 Spring Framework 4 MyBatis framework 5 Java中文社群
-
迭代器keySet
Iterator<Integer> iterator = map.keySet().iterator(); while (iterator.hasNext()) { Integer key = iterator.next(); System.out.println(key); System.out.println(map.get(key)); }
结果同上
-
ForEach EntrySet
for (Map.Entry<Integer, String> entry : map.entrySet()) { System.out.println(entry.getKey()); System.out.println(entry.getValue()); }
同上
-
ForEach KeySet
for(Integer key : map.keySet()){ System.out.println(key); System.out.println(map.get(key)); }
-
Lambda
map.forEach((key, value)) -> { System.out.println(key); System.out.println(value); });
-
Streams API 单线程
map.entrySet().stream().forEach(entry) -> { System.out.println(entry.getKey()); System.out.println(entry.getValue()); });
-
Streams API 多线程
map.entrySet().parallelStream().forEach(entry) -> { //同上 });
结果:
4
MyBatis framework
5
Java中文社群
1
Java
2
JDK
3
Spring Framework
- 性能分析
使用迭代器或是forEach循环的EntrySet
方法,性能是相同的。
使用…的keySet
方法,性能也相同。
但是两种EntrySet比keySet方法性能高,是因为KeySet
在循环时使用了 map.get(key)
,而 map.get(key)
相当于又遍历了一遍 Map 集合去查询 key
所对应的值。(为什么要用“又”这个词?那是因为在使用迭代器或者 for 循环时,其实已经遍历了一遍 Map 集合了,因此再使用 map.get(key)
查询时,相当于遍历了两遍。)
而 EntrySet
只遍历了一遍 Map 集合,之后通过代码“Entry<Integer, String> entry = iterator.next()”把对象的 key
和 value
值都放入到了 Entry
对象中,因此再获取 key
和 value
值时就无需再遍历 Map 集合,只需要从 Entry
对象中取值就可以了。
所以,EntrySet
的性能比 KeySet
的性能高出了一倍,因为 KeySet
相当于循环了两遍 Map 集合,而 EntrySet
只循环了一遍。
- 安全性分析
我们不能在遍历中使用集合 map.remove()
来删除数据,这是非安全的操作方式,但我们可以使用迭代器的 iterator.remove()
的方法来删除数据,这是安全的删除集合的方式。同样的我们也可以使用 Lambda 中的 removeIf
来提前删除数据,或者是使用 Stream 中的 filter
过滤掉要删除的数据进行循环,这样都是安全的,当然我们也可以在 for
循环前删除数据在遍历也是线程安全的。
ConcurrentHashMap和HashTable
-
底层数据结构:1.7采用分段的数组+链表,1.8采用和HashMap一样。
-
实现线程安全的方式(※):①在 JDK1.7 的时候,
ConcurrentHashMap
(分段锁) 对整个桶数组进行了分割分段(Segment
),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 到了 JDK1.8 的时候已经摒弃了Segment
的概念,而是直接用Node
数组+链表+红黑树的数据结构来实现,并发控制使用synchronized
和 CAS 来操作。(JDK1.6 以后 对synchronized
锁做了很多优化) 整个看起来就像是优化过且线程安全的HashMap
,虽然在 JDK1.8 中还能看到Segment
的数据结构,但是已经简化了属性,只是为了兼容旧版本。②Hashtable
(同一把锁) :使用synchronized
来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
段(Segment
),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 到了 JDK1.8 的时候已经摒弃了 Segment
的概念,而是直接用 Node
数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized
和 CAS 来操作。(JDK1.6 以后 对 synchronized
锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap
,虽然在 JDK1.8 中还能看到 Segment
的数据结构,但是已经简化了属性,只是为了兼容旧版本。② Hashtable
(同一把锁) :使用 synchronized
来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。