JDK源码解析---ConcurrentHashMap

1.概述

HashMap在多线程编程中是线程不安全的,而Hashtable由于使用了synchronized修饰方法而导致执行效率不高;因此,在concurrent包中,实现了ConcurrentHashMap以使在多线程编程中可以使用一个高性能的线程安全HashMap方案,将锁的粒度缩小,不像HashTable一样锁整个表。1.7和1.8采用不同的方式实现。下面先来说一下两者的不同。然后主要介绍jdk8中的实现

2.concurrentHashMap1.7与1.8的区别

2.1锁的机制不同

1.7中维护了一个Segment数组,及将整个Hash表划分成多个分段。而每个Segment元素对应一个散列表。这样在put操作的时候会先根据hash算法定位到Segment元素。然后锁住对应的散列表。比起HashTable的锁整个表的粒度上变小了。这样就提高了并发度。

1.8中并没有维护一个Segment数组 而是采用和1.8中HashMap类似的数据结构 数组+链表+红黑树。利用CAS和synchronized来锁一链表或一棵树。即锁住散列表的一个格子。这样在锁的粒度上比1.7又更小了一点。提高了并发度。

2.2 数据结构不同

1.7采用Segment数组 + HashEntry数组的方式实现。Segment元素对应一个HashEntry数组,HashEntry是一个链表。Segment数组默认大小是16,HashEntry数组最小值为2,两种数组的长度都要满足2的n次方。且Segment数组的大小看如下的代码

int size =1; while(size < concurrencyLevel) { ++a; size <<=1; } 根据concurrencyLevel大小。取第一个比concurrencyLevel大的2的n次方的数字。

当然concurrencyLevel最大只能用16位的二进制来表示,即65536,换句话说,Segment的大小最多65536个。

1.8采用数组+链表+红黑树的结构。默认数组大小为16

3.类图

在这里插入图片描述

继承了AbstractMap 实现了Map的一些基本操作

实现了ConcurrentMap接口、序列化接口

4.属性

静态常量

private static final int MAXIMUM_CAPACITY = 1 << 30;//最大散列表容量

private static final int DEFAULT_CAPACITY = 16;//默认大小

static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//用于转数组或其他和数组相关的方法 最大数组容量

private static final int DEFAULT_CONCURRENCY_LEVEL = 16;//默认支持16个并发 1.8中并没有使用到这个常量。1.7这个代表着默认的Segement数组的大小默认16.
private static final float LOAD_FACTOR = 0.75f;//装载因子

static final int TREEIFY_THRESHOLD = 8;//树化的阈值

static final int UNTREEIFY_THRESHOLD = 6;//链化的阈值

static final int MIN_TREEIFY_CAPACITY = 64;//最小树化的散列表大小 达到64并且一个结点链表长度到8才会树化

private static final int MIN_TRANSFER_STRIDE = 16;// 最少迁移的Hash槽个数

private static int RESIZE_STAMP_BITS = 16;// todo

private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;// 可执行扩容的线程数

private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;// todo

static final int MOVED = -1; // 表示正在执行扩容
static final int TREEBIN = -2; // 表示这是一个红黑树结点
static final int RESERVED = -3; // hash for transient reservations
static final int HASH_BITS = 0x7fffffff; // 用于得到32位的Hash值

static final int NCPU = Runtime.getRuntime().availableProcessors();//处理器数量

属性字段

transient volatile Node<K, V>[] table;//散列表

private transient volatile Node<K, V>[] nextTable;// 过渡数组 在扩容的时候用到

private transient volatile long baseCount;// 保存键值对的个数


//    默认为0
    //    hash表初始化或扩容时的一个控制位标识量。
    //    负数代表正在进行初始化或扩容操作
    //     -1代表正在初始化
    //     -N 表示有N-1个线程正在进行扩容操作
    //    当初始化或扩容完成后 这个数值表示下一次进行扩容的阈值
private transient volatile int sizeCtl;

private transient volatile int transferIndex;//扩容时候 要分割的nextTable的索引

private transient volatile int cellsBusy;// 自旋锁 扩容或者创建CounterCells 时使用

 private transient volatile CounterCell[] counterCells;// 计数器表 记录了散列表中对应的每一个槽包含的结点的数量

private transient KeySetView<K, V> keySet;//键的集合
private transient ValuesView<K, V> values;//值的集合
private transient EntrySetView<K, V> entrySet;//键值结点对的集合

5.node

实现了Map的Entry结点 与HashMap不同的是 node的value加上了 volatile修饰

下一个结点也加上了volatile修饰 保证了可见性

6.静态工具方法

这些方法都是在ConcurrentHashMap中使用的,没有提供给外部调用

6.1 spread

再hash 将计算好的hash值留下低32位。然后高16位与低16位进行异或。

static final int spread(int h) {
    
    
    // HASH_BITS 是32个1 也就是留下了低32位的hash值。
    // 再hash hash值高16与它的低16位进行hash
    return (h ^ (h >>> 16)) & HASH_BITS;
}

6.2 tableSizeFor

找到比给定容量c大的最小的2的n次方的值。

private static final int tableSizeFor(int c) {
    
    
    int n = c - 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;
}

6.3 comparableClassFor

这个方法就是判断传入的Object对象x是否实现了****Comparable*接口.如果传入的对象时String类型,它自然而然实现了Comparable接口,所以直接跳过判断.但是对于其他的类,比方说我们自己写了一个类对象,然后存在HashMap中,但是就HashMap来说它并不知道我们有没有实现Comparable*接口,甚至都不知道我们Comparable接口中有没有用泛型,泛型具体用的是哪个类.

static Class<?> comparableClassFor(Object x) {
    
     //返回x的类
        if (x instanceof Comparable) {
    
    
            Class<?> c;
            Type[] ts, as;
            Type t;
            ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
    
    // 判断是否有直接实现的接口
                for (int i = 0; i < ts.length; ++i) {
    
    // 遍历直接实现的接口
                    if (((t = ts[i]) instanceof ParameterizedType) && // 该接口实现了泛型
                            ((p = (ParameterizedType) t).getRawType() == // 获取接口不带参数部分的类型对象
                                    Comparable.class) &&//  该类型是Comparable
                            (as = p.getActualTypeArguments()) != null && // 获取泛型参数数组
                            as.length == 1 && as[0] == c) // 只有一个泛型参数,且该实现类型是该类型本身
                        return c; // 返回该类型
                }
            }
        }
        return null;
    }

6.4 compareComparables

两个对象的比较

static int compareComparables(Class<?> kc, Object k, Object x) {
    
    
    return (x == null || x.getClass() != kc ? 0 ://若x为空 或者x的类不是kc 返回0 否者k和x进行比较
            ((Comparable) k).compareTo(x));
}

6.5 tabAt

寻找数组tab 在内存中i位置的数据

static final <K, V> Node<K, V> tabAt(Node<K, V>[] tab, int i) {
    
    //寻找数组tab 在内存中i位置的数据
    return (Node<K, V>) U.getObjectVolatile(tab, ((long) i << ASHIFT) + ABASE);
}

6.6 casTabAt

cas修改tab数组 内存中i位置的数据 期望从c修改为v

static final <K, V> boolean casTabAt(Node<K, V>[] tab, int i,
                                     Node<K, V> c, Node<K, V> v) {
    
    // cas修改tab数组 内存中i位置的数据 期望从c修改为v
    return U.compareAndSwapObject(tab, ((long) i << ASHIFT) + ABASE, c, v);
}

6.7 setTabAt

cas设置tab数组 内存中i位置的数据为v

static final <K, V> void setTabAt(Node<K, V>[] tab, int i, Node<K, V> v) {
    
    //cas设置tab数组 内存中i位置的数据为v
    U.putObjectVolatile(tab, ((long) i << ASHIFT) + ABASE, v);
}

7.构造函数

7.1 无参构造函数

public ConcurrentHashMap() {
    
    
}

7.2 ConcurrentHashMap(int initialCapacity)

jdk8中 散列表是延迟初始化的

public ConcurrentHashMap(int initialCapacity) {
    
    
    if (initialCapacity < 0)
        throw new IllegalArgumentException();
    int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?//设置为大于参数initialCapacity的最小的2的n次方的数
            MAXIMUM_CAPACITY :
            tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
    this.sizeCtl = cap;//和HashMap一样 初始化的时候没有对散列表进行初始化 sizeCtl初始化为下一次要扩容的大小
}

7.3 ConcurrentHashMap(Map<? extends K, ? extends V> m)

public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
    
    //这里下次要扩容的大小 sizeCtl 将集合m中的元素添加进map中
    this.sizeCtl = DEFAULT_CAPACITY;//默认容量16
    putAll(m);
}

7.4 ConcurrentHashMap(int initialCapacity, float loadFactor)

指定初始容量和装载因子

public ConcurrentHashMap(int initialCapacity, float loadFactor) {
    
    
    this(initialCapacity, loadFactor, 1);
}

7.5 ConcurrentHashMap(int initialCapacity,

                         float loadFactor, int concurrencyLevel)

指定初始容量和装载因子以及并发度,并发等级只是用来干扰初始化容量的

public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel) {
    
    
    if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
        throw new IllegalArgumentException();
    //若参数初始容量 小于 并发等级 则将初始容量设置为并发等级的大小。
    if (initialCapacity < concurrencyLevel)   // Use at least as many bins
        initialCapacity = concurrencyLevel;   // as estimated threads
    long size = (long) (1.0 + (long) initialCapacity / loadFactor);//根据初始容量和装载因子 计算出需要的散列表大小。
    // 实际的散列表大小 要满足2的n次方 所以需要调用tableSizeFor方法 找到比size大的 数中最小的2的n次方
    int cap = (size >= (long) MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int) size);
    this.sizeCtl = cap;//设置下次要扩容的大小为cap
}

8.主要方法(1.8)

8.1 put

public V put(K key, V value) {
    
    
    return putVal(key, value, false);
}
8.1.1 putVal
  1. 首先判断是否是第一次put,在第一次put之前散列表还没有进行初始化。所以需要对散列表进行初始化 执行initTable()方法
  2. 根据key计算hash值 然后对其进行再散列后。找到应该存储在散列表中的位置。若这个位置是空的,则执行cas将新的节点添加到散列表中的这个槽位上。
  3. 如果这个位置的hash是MOVED 即 -1 说明这是一个forwarding node表示正在扩容。那么这个正在执行put操作的线程会去帮助扩容。执行helpTransfer方法
  4. 当前散列表不在扩容,那么就synchronized锁住当前这个槽位。然后判断这个节点是链表节点 还是红黑树节点。根据不同的节点类型。执行不同的put逻辑。
final V putVal(K key, V value, boolean onlyIfAbsent) {
    
    
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());//对key进行再散列
    int binCount = 0;
    for (Node<K, V>[] tab = table; ; ) {
    
    
        Node<K, V> f;
        int n, i, fh;
        if (tab == null || (n = tab.length) == 0)//第一次put 散列表还没初始化
            tab = initTable();//则执行初始化
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
    
    //根据hash得到索引 根据索引得到散列表中这个索引的元素 如果是空的
            if (casTabAt(tab, i, null,
                    new Node<K, V>(hash, key, value, null)))//则执行cas将新的结点添加到对应的槽的位置中
                break;                   // no lock when adding to empty bin
        } else if ((fh = f.hash) == MOVED)//正在扩容
            tab = helpTransfer(tab, f);//这个线程帮助扩容 todo 了解细节
        else {
    
    
            V oldVal = null;
            synchronized (f) {
    
    //锁一个结点
                if (tabAt(tab, i) == f) {
    
    
                    if (fh >= 0) {
    
    //这个结点是链表结点
                        binCount = 1;
                        for (Node<K, V> e = f; ; ++binCount) {
    
    
                            K ek;
                            if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                            (ek != null && key.equals(ek)))) {
    
    //已存在key
                                oldVal = e.val;
                                if (!onlyIfAbsent)//onlyIfAbsent 为false则直接修改对应的value
                                    e.val = value;
                                break;
                            }
                            Node<K, V> pred = e;
                            if ((e = e.next) == null) {
    
    //添加在链尾
                                pred.next = new Node<K, V>(hash, key,
                                        value, null);
                                break;
                            }
                        }
                    } else if (f instanceof TreeBin) {
    
    //如果是红黑树
                        Node<K, V> p;
                        binCount = 2;
                        if ((p = ((TreeBin<K, V>) 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);//结点数量加1 若超过阈值 则扩容
    return null;
}
8.1.1.1 initTable

初始化散列表。

private final Node<K, V>[] initTable() {
    
    
    Node<K, V>[] tab;
    int sc;
    while ((tab = table) == null || tab.length == 0) {
    
    
        if ((sc = sizeCtl) < 0)// 其他线程正在创建
            Thread.yield(); // lost initialization race; just spin
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
    
     // -1表示正在初始化 将sizeCtl 设置为-1 
            try {
    
    
                if ((tab = table) == null || tab.length == 0) {
    
    //散列表为空
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;//散列表的大小
                    @SuppressWarnings("unchecked")
                    Node<K, V>[] nt = (Node<K, V>[]) new Node<?, ?>[n];//新建散列表
                    table = tab = nt;
                    sc = n - (n >>> 2);//扩容阈值为容量*0.75 也就是n - n/4 
                }
            } finally {
    
    
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}
8.1.1.2addCount

还没有累加单元数组,那么就直接在baseCount上进行cas累加

若累加失败,并且还没有累加单元数组则创建累加单元数组 再次累加

累加完之后判断是否需要扩容

private final void addCount(long x, int check) {
    
    
    CounterCell[] as;
    long b, s;
    // counterCells 不为空
    if ((as = counterCells) != null ||
            // 还没有 向baseCount累加
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
    
    
        CounterCell a;
        long v;
        int m;
        boolean uncontended = true;
        // 还没有累加单元数组
        if (as == null || (m = as.length - 1) < 0 ||
                // 还没有累加单元
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                // cell cas 增加计数失败
                !(uncontended =
                        U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
    
    
            // 创建累加单元数组和cell 累加重试
            fullAddCount(x, uncontended);
            return;
        }
        if (check <= 1)
            return;
        // 获取元素个数
        s = sumCount();
    }
    if (check >= 0) {
    
    
        Node<K, V>[] tab, nt;
        int n, sc;
        // 看看是否需要扩容
        while (s >= (long) (sc = sizeCtl) && (tab = table) != null &&
                (n = tab.length) < MAXIMUM_CAPACITY) {
    
    
            int rs = resizeStamp(n);
            if (sc < 0) {
    
    
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                        transferIndex <= 0)
                    break;
                // newTable 已经创建了 帮助扩容
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                    transfer(tab, nt);
            }
            // 需要扩容 这是 newTable 还未创建
            else if (U.compareAndSwapInt(this, SIZECTL, sc,
                    (rs << RESIZE_STAMP_SHIFT) + 2))
                transfer(tab, null);
            s = sumCount();
        }
    }
}

8.2 helpTransfer

final Node<K, V>[] helpTransfer(Node<K, V>[] tab, Node<K, V> f) {
    
    
    Node<K, V>[] nextTab;
    int sc;
    //如果table不是空的 并且f结点是一个转移类型的结点 数据校验
    if (tab != null && (f instanceof ForwardingNode) &&
            //并且f结点的nextTable不为空 同样也是在数据校验。
            (nextTab = ((ForwardingNode<K, V>) f).nextTable) != null) {
    
    
        // 尝试帮助扩容
        int rs = resizeStamp(tab.length); //根据tab的大小 得到一个标识符号
        while (nextTab == nextTable && table == tab &&
                (sc = sizeCtl) < 0) {
    
    //如果 nextTab 没有被并发修改 且 tab 也没有被并发修改 且 sizeCtl  < 0 (说明还在扩容)
            // 如果 sizeCtl 无符号右移  16 不等于 rs ( sc前 16 位如果不等于标识符,则标识符变化了)
            // 或者 sizeCtl == rs + 1  (扩容结束了,不再有线程进行扩容)(默认第一个线程设置 sc ==rs 左移 16 位 + 2,当第一个线程结束扩容了,就会将 sc 减一。这个时候,sc 就等于 rs + 1)
            // 或者 sizeCtl == rs + 65535  (如果达到最大帮助线程的数量,即 65535)
            // 或者转移下标正在调整 (扩容结束)
            // 结束循环,返回 table
            if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                break;
            if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
    
    // 增加一个线程帮助扩容
                transfer(tab, nextTab);//进行转移
                break;
            }
        }
        return nextTab;
    }
    return table;
}
8.2.1 transfer
private final void transfer(Node<K, V>[] tab, Node<K, V>[] nextTab) {
    
    
    int n = tab.length, stride;
    // 需要迁移多少个hash槽 最少MIN_TRANSFER_STRIDE 16个
    // 计算table的长度/8再处于cpu的核心数 如果小于16的化 则用16。
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE; // subdivide range
    if (nextTab == null) {
    
                // nextTab为空
        try {
    
    
            @SuppressWarnings("unchecked")
            Node<K, V>[] nt = (Node<K, V>[]) new Node<?, ?>[n << 1];//两倍扩容
            nextTab = nt;
        } catch (Throwable ex) {
    
          // try to cope with OOME
            sizeCtl = Integer.MAX_VALUE;
            return;
        }
        nextTable = nextTab;
        transferIndex = n;
    }
    int nextn = nextTab.length;
    // 创建一个 fwd 节点,用于占位。当别的线程发现这个槽位中是 fwd 类型的节点,则跳过这个节点。
    ForwardingNode<K, V> fwd = new ForwardingNode<K, V>(nextTab);
    boolean advance = true; //推进状态
    boolean finishing = false; // to ensure sweep before committing nextTab
    for (int i = 0, bound = 0; ; ) {
    
    
        Node<K, V> f;
        int fh;
        while (advance) {
    
    
            int nextIndex, nextBound;
            if (--i >= bound || finishing)
                advance = false;
            else if ((nextIndex = transferIndex) <= 0) {
    
    
                i = -1;
                advance = false;
            }
            // CAS 修改 transferIndex,即 length - 区间值,留下剩余的区间值供后面的线程使用
            else if (U.compareAndSwapInt
                    (this, TRANSFERINDEX, nextIndex,
                            nextBound = (nextIndex > stride ?
                                    nextIndex - stride : 0))) {
    
    
                bound = nextBound;// 这个值就是当前线程可以处理的最小当前区间最小下标
                i = nextIndex - 1;// 初次对i 赋值,这个就是当前线程可以处理的当前区间的最大下标
                advance = false;
            }
        }
        if (i < 0 || i >= n || i + n >= nextn) {
    
    
            int sc;
            if (finishing) {
    
     // 如果完成了扩容
                nextTable = null; // 删除成员变量
                table = nextTab;// 更新table
                sizeCtl = (n << 1) - (n >>> 1); // 更新阈值
                return;
            }
            if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
    
    // 尝试将 sc -1. 表示这个线程结束帮助扩容了,将 sc 的低 16 位减一。
                if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)// 如果 sc - 2 不等于标识符左移 16 位。如果他们相等了,说明没有线程在帮助他们扩容了。也就是说,扩容结束了。
                    return;
                finishing = advance = true;
                i = n; // recheck before commit
            }
        } else if ((f = tabAt(tab, i)) == null)// 获取老 tab i 下标位置的变量,如果是 null,就使用 fwd 占位。
            advance = casTabAt(tab, i, null, fwd);// 如果成功写入 fwd 占位,再次推进一个下标
        else if ((fh = f.hash) == MOVED)// 如果不是 null 且 hash 值是 MOVED。
            advance = true; // 说明别的线程已经处理过了,再次推进一个下标
        else {
    
    
            // 到这里,说明这个位置有实际值了,且不是占位符。对这个节点上锁。为什么上锁,防止 putVal 的时候向链表插入数据
            synchronized (f) {
    
    
                if (tabAt(tab, i) == f) {
    
    
                    Node<K, V> ln, hn;// ln转移之后原位置的头结点  hn原位置+16 的头结点
                    if (fh >= 0) {
    
     // 链表结点
                        int runBit = fh & n;
                        Node<K, V> lastRun = f;
                        for (Node<K, V> p = f.next; p != null; p = p.next) {
    
    
                            int b = p.hash & n;
                            if (b != runBit) {
    
    
                                runBit = b;
                                lastRun = p;
                            }
                        }
                        if (runBit == 0) {
    
    
                            ln = lastRun;
                            hn = null;
                        } else {
    
    
                            hn = lastRun;
                            ln = null;
                        }
                        // 生成两个链表 根据hash & n最高位是1和0来判断
                        for (Node<K, V> p = f; p != lastRun; p = p.next) {
    
    
                            int ph = p.hash;
                            K pk = p.key;
                            V pv = p.val;
                            if ((ph & n) == 0)
                                ln = new Node<K, V>(ph, pk, pv, ln);
                            else
                                hn = new Node<K, V>(ph, pk, pv, hn);
                        }
                        //赋值
                        setTabAt(nextTab, i, ln);
                        setTabAt(nextTab, i + n, hn);
                        setTabAt(tab, i, fwd);
                        advance = true; // 扩容完成
                    } else if (f instanceof TreeBin) {
    
    //树结点
                        TreeBin<K, V> t = (TreeBin<K, V>) f;
                        TreeNode<K, V> lo = null, loTail = null;
                        TreeNode<K, V> hi = null, hiTail = null;
                        int lc = 0, hc = 0;
                        for (Node<K, V> e = t.first; e != null; e = e.next) {
    
    
                            int h = e.hash;
                            TreeNode<K, V> p = new TreeNode<K, V>
                                    (h, e.key, e.val, null, null);
                            if ((h & n) == 0) {
    
    
                                if ((p.prev = loTail) == null)
                                    lo = p;
                                else
                                    loTail.next = p;
                                loTail = p;
                                ++lc;
                            } else {
    
    
                                if ((p.prev = hiTail) == null)
                                    hi = p;
                                else
                                    hiTail.next = p;
                                hiTail = p;
                                ++hc;
                            }
                        }
                        ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                                (hc != 0) ? new TreeBin<K, V>(lo) : t;
                        hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                                (lc != 0) ? new TreeBin<K, V>(hi) : t;
                        setTabAt(nextTab, i, ln);
                        setTabAt(nextTab, i + n, hn);
                        setTabAt(tab, i, fwd);
                        advance = true;
                    }
                }
            }
        }
    }
}

8.3 get

  1. 首先根据key计算hash值然后再散列。

  2. 根据再散列后的值定位到散列表中的位置。

  3. 如果这个位置的节点的hash值等于再散列后的hash值并且这个位置的key等于参数key 则直接返回这个节点的value

  4. 如果这个节点的hash值小于0 则执行find方法 根据不同的节点类型执行不同的逻辑。

  5. 否则这里是个链表,则遍历这个链表,返回找到的节点的value值

public V get(Object key) {
    
    
    Node<K, V>[] tab;
    Node<K, V> e, p;
    int n, eh;
    K ek;
    int h = spread(key.hashCode());//再散列
    if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
    
    //根据再散列后的值取后tab.length位 得到存储再散列表中这个位置的结点
        if ((eh = e.hash) == h) {
    
    //如果这个结点的hash值等于 key再散列后的hash值
            if ((ek = e.key) == key || (ek != null && key.equals(ek)))//并且这个结点的key与参数key相同
                return e.val;//返回key对应的value
        } else if (eh < 0)// eh小于0  说明 正在扩容或者 是treebin 下面的find根据结点的不同 有不同的实现。
            return (p = e.find(h, key)) != null ? p.val : null;
        while ((e = e.next) != null) {
    
    //头结点不是要找到的key 遍历链表
            if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}

8.4 size

public int size() {
    
    
    long n = sumCount();
    return ((n < 0L) ? 0 :
            (n > (long) Integer.MAX_VALUE) ? Integer.MAX_VALUE :
                    (int) n);
}

若累加单元数组不为空,则将其全部累加起来。

final long sumCount() {
    
    
    CounterCell[] as = counterCells;
    CounterCell a;
    long sum = baseCount;
    if (as != null) {
    
    
        for (int i = 0; i < as.length; ++i) {
    
    
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    return sum;
}

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/108588824
今日推荐