JAVA Map 源码分析
一、map:Map是用来存储键值对的数据结构,在数组中通过数组下标来对其内容进行索引。
HashMap: 根据键的HashCode值存储数据,使用的数据结构是链表散列(数组+链表)访问速度很快。父类是AbstractMap。
特点是线程不安全、允许空键值,改用containsvalue和containsKey。
冲突解决方法是链地址法(它有一个桶的概念,对于Entry数组而言,数组每个元素处存储的是链表,一个链表就是一个桶,使用addEntry()方法添加新元素时总是将新元素添加在链表的表头)
扩容:当size超过0.75(变量loadFactor默认是0.75)*threshold的时候会调整大小,默认是扩展为2倍。
HashTable:继承Dictionary,方法被synchronized修饰,所以是一个线程安全的实现,但是性能略低于HashMap。使用的迭代器是Enumeration,会在别的线程修改元素的时候抛出ConcurrentModificationException异常。(现在已经被下面这个取代了)
concurrentHashMap:产生于JDK1.5之后,用于替代HashTable的线程安全的HashMap。concurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求。大量利用了volatile,final和cas等lock-free技术来减少锁竞争对于性能的影响。在JDK6,7,8的实现都不一样。在JDK8中,利用CAS来替代之前的segment(锁段)概念,底层用数组+链表+红黑树的方法实现,增加了很多辅助类来支持并发。
二、HashMap
(1)HashMap继承AbstractMap,实现了Map、Cloneable和Serializable接口。
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
(2)定义常量:初始容量为16,最大容量是2的30次方,负载因子0.75,阈值threshold = length * Load factor
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final int MAXIMUM_CAPACITY = 1 << 30;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
(3)底层实现:数组+链表
Node[] 就是哈希桶数组,实现了Map.Entry接口,本质是就是一个映射(键值对)。哈希桶数组table的长度length大小必须为2的n次方
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
(4)hash方法的实现:在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在 数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode