JDK源码之HashMap与ConcurrentHashMap区别

HashMap

  • 结构:数组加链表。

JDK源码之HashMap与ConcurrentHashMap区别_第1张图片

  • 基本插入步骤(不包含扩容)

1.通过数组长度n-1和插入key的hash求余,即(n-1)&hash得到准备放入的数组节点中,

2.如果当前数组节点中无数据,则直接放入node

3.如果有值,则放入node链表尾部

  • 查找步骤

1.通过hash求余查询到数组节点

2.从前到后遍历node节点,比对node中hash值与key值必须相等则匹配成功返回。

  • 相关参数
public class HashMap extends AbstractMap
    implements Map, Cloneable, Serializable {
//默认初始化数组长度,2的四次方为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 
//数组最大长度2的30次方1,073,741,824,约10亿
static final int MAXIMUM_CAPACITY = 1 << 30;
//数组扩容因子,大于75%时数组进行扩容,原有扩容后长度=长度*2
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//数组中的单个链表长度大于等于8时,链表结构换为红黑树存储
//根据概率轮计算,当长度为8时,树的读写效率高于链表
static final int TREEIFY_THRESHOLD = 8;
//当数组中红黑树节点数小于等于6个时,红黑树换为链表结构存储
static final int UNTREEIFY_THRESHOLD = 6;
//红黑树存储最小队列长度
//为防止与数组扩容冲突,数组长度必须大于等64,才能使用红黑树结构
static final int MIN_TREEIFY_CAPACITY = 64;

//node数组
//transient表示瞬态,不参数序列化
transient Node[] table;
//链表节点
static class Node implements Map.Entry {
        //节点hash值
        final int hash;
        //节点key
        final K key;
        //节点value
        V value;
        //下一个节点
        Node next;
...
}

ConcurrentHashMap

  • 结构:数组+链表,与hashMap一致。
版本 1.7 1.8
结构 Segment数组+HashEntry数组+HashEntry链表 node数组+node链表/红黑树
锁类型 重入锁ReentrantLock sychronized
锁节点 锁住HashEntry数组 锁住node链表
  • 相关参数
    // node数组最大容量:2^30=1073741824
    private static final int MAXIMUM_CAPACITY = 1 << 30;
    // 默认初始值,必须是2的幕数
    private static final int DEFAULT_CAPACITY = 16
    //数组可能最大值,需要与toArray()相关方法关联
    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //并发级别,遗留下来的,为兼容以前的版本
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
    // 负载因子
    private static final float LOAD_FACTOR = 0.75f;
    // 链表转红黑树阀值,> 8 链表转换为红黑树
    static final int TREEIFY_THRESHOLD = 8;
    //树转链表阀值,小于等于6(tranfer时,lc、hc=0两个计数器分别++记录原bin、新binTreeNode数量,<=UNTREEIFY_THRESHOLD 则untreeify(lo))
    static final int UNTREEIFY_THRESHOLD = 6;
    static final int MIN_TREEIFY_CAPACITY = 64;
    private static final int MIN_TRANSFER_STRIDE = 16;
    private static int RESIZE_STAMP_BITS = 16;
    // 2^15-1,help resize的最大线程数
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
    // 32-16=16,sizeCtl中记录size大小的偏移量
    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
    // forwarding nodes的hash值
    static final int MOVED     = -1;
    // 树根节点的hash值
    static final int TREEBIN   = -2;
    // ReservationNode的hash值
    static final int RESERVED  = -3;
    // 可用处理器数量
    static final int NCPU = Runtime.getRuntime().availableProcessors();
    //存放node的数组
    transient volatile Node[] table;
    //控制标识符,用来控制table的初始化和扩容的操作,不同的值有不同的含义
    //当为负数时:-1代表正在初始化,-N代表有N-1个线程正在 进行扩容
    //当为0时:代表当时的table还没有被初始化
    //当为正数时:表示初始化或者下一次进行扩容的大小
    private transient volatile int sizeCtl;
    
    
    /**
    * 插入数据
    */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
            if (key == null || value == null) throw new NullPointerException();
            int hash = spread(key.hashCode());
            int binCount = 0;
            for (Node[] tab = table;;) {
                Node f; int n, i, fh;
                
                if (tab == null || (n = tab.length) == 0)
                    //数组为空初始化
                    tab = initTable();
                else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                    //准备放入的数组节点位置为空,使用CAS放入
                    if (casTabAt(tab, i, null,
                                 new Node(hash, key, value, null)))
                        break;                   // no lock when adding to empty bin
                }
                else if ((fh = f.hash) == MOVED)
                    //map增在调整大小,则使用扩容添加
                    tab = helpTransfer(tab, f);
                else {
                    //数组节点冲突使用同步锁
                    V oldVal = null;
                    synchronized (f) {
                        //确认冲突节点没变
                        if (tabAt(tab, i) == f) {
                            //插入链表中
                            if (fh >= 0) {    
                                binCount = 1;
                                for (Node e = f;; ++binCount) {
                                    K ek;
                                    if (e.hash == hash &&
                                        ((ek = e.key) == key ||
                                         (ek != null && key.equals(ek)))) {
                                        oldVal = e.val;
                                        if (!onlyIfAbsent)
                                            e.val = value;
                                        break;
                                    }
                                    Node pred = e;
                                    if ((e = e.next) == null) {
                                        pred.next = new Node(hash, key,
                                                                  value, null);
                                        break;
                                    }
                                }
                            }
                            else if (f instanceof TreeBin) {
                                Node p;
                                binCount = 2;
                                if ((p = ((TreeBin)f).putTreeVal(hash, key,
                                                               value)) != null) {
                                    oldVal = p.val;
                                    if (!onlyIfAbsent)
                                        p.val = value;
                                }
                            }
                        }
                    }
                    if (binCount != 0) {
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);
                        if (oldVal != null)
                            return oldVal;
                        break;
                    }
                }
            }
            addCount(1L, binCount);
            return null;
        }

你可能感兴趣的:(JDK)