HashMap:数组+链表
真正存放数据的地方:
Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE
初始化的大小 1<<4
最大值 1<<30
默认负载因子 0.75
扩容涉及到rehash 复制数据等操作,非常消耗性能。
jdk1.7
当Hash冲突严重时,在桶上形成的链表会变得原来越长,这样查询时的效率就会越来越低,时间复杂度O(n)。
Jdk1.8
红黑树 TREEIFY_THRESHOLD 用于判断将链表转换为红黑树的阈值。
HashEntry 修改为Node
红黑树提高了查询效率 提高到了O(logn)
建议这样取值:
Iterator<Map.Entry<String, Integer>> entryIterator = map.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Integer> next = entryIterator.next();
System.out.println("key=" + next.getKey() + " value=" + next.getValue());
}
ConcurrentHashMap:数组+链表 解决并发问题
由Segment数组、HashEntry组成
数组+链表
存放数据时首先需要定位到具体的Segment中。
ConcurrentHashMap支持CurrencyLevel(Segment数组数量)的线程并发。
将key通过Hash 之后定位到具体的Segment,再通过一次Hash定位到具体的元素上。
HashEntry中的value用volatile修饰,确保可见性。
JDK1.7存在的问题:遍历链表效率太低
JDK1.8 采用了CAS+synchronized来保证并发安全性。
存放数据的HashEntry 改为Node。
用红黑树查询效率O(logn)
取消了ReentrantLock 改为了 synchronized