HashSet源码分析

HashSet源码分析

java模拟数组+链表:java模拟数组+链表 - CoderDreams - 博客园 (cnblogs.com)

结论

  1. HashSet底层是HashMap

  2. 添加一个元素时:先得到Hash值 ==> 转成索引

  3. 找到存储数据表table,检查这个索引位置是否已经存放有元素

    如果没有,直接加入

    如果有,调用equals比较

    ​ 如果相同放弃添加

    ​ 如果不相同添加到节后最后

  4. 如果链表元素个数超过8,并且table大小>=64就会树化

第一次add()源码分析

此次是需要扩容的

构造函数

private transient HashMap<E,Object> map;

public HashSet() {
    map = new HashMap<>();
}

实际是维护了一个HashMap<>


add()

public boolean add(E e) { // 假设添加字符串"item"
    
    // PRESENT
    /*
    Dummy value to associate with an Object in the backing Map
    在返回的 Map 中关联一个对象的虚值
    只是一个占位的虚值,是一个Object的静态final实体类对象
    private static final Object PRESENT = new Object();
    */
    return map.put(e, PRESENT)==null;
    // 成功返回值为null
    // null == null为true,返回true
}

实际上是调用了HashMap的put()

put()

		// "item"	 Object对象
public V put(K key, V value) {
    // 调用了putVal()
    // hash(key)调用了hash()
    return putVal(hash(key), key, value, false, true);
    // 接收到putVal方法返回值(成功为null),回到add()
}

// hash()
static final int hash(Object key) {
        int h;
    	// 如果key为null返回0
    	// 如果key不为null返回(h = key.hashCode()) ^ (h >>> 16)
    	// key.hashCode()调用了hashCode()
    	// public native int hashCode();
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

// putVal()		
/*		
		onlyIfAbsent– if true, don't change existing value evict
        onlyIfAbsent如果为真,则不更改现有值
        
        evit – if false, the table is in creation mode.
        evit如果为false,表示表处于创建模式。
        */
	// hash("item")的返回值 "item" Object			fasle				true
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    	// 定义了一些辅助变量:一个Node<K,V>[](节点数组),一个Node<K,V>(节点),两个int值
        Node<K,V>[] tab; Node<K,V> p; int n, i;
    	// transient Node<K,V>[] table;
    	/*
    	tab = table;
    	n = tab.length;
    	赋值并判断
    	如果table为null或者table长度为0时
    	也就是第一次进入这个方法时,第一次扩容
    	*/
        if ((tab = table) == null || (n = tab.length) == 0)
            // 调用了resize()方法,往下翻
            // resize()后table变成16大小并将返回值赋给tab
            // n为16
            n = (tab = resize()).length;
    	/*1.根据参数hash(hash(key))计算参数key应该存放到tab的哪个索引位置
    	 	并把这个位置的对象赋给 Node<K,V> p
    	  2.再判断这个p是否为null
    	*/
        if ((p = tab[i = (n - 1) & hash]) == null)
            // 如果p为null,表示还没有存放元素,就创建一个Node放进tab[i]
            // hash = hash(key)  key = "item"	 value = Object对象
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            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) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
    	// ++修改次数
        ++modCount;
    	// ++size并判断有没有超过临界值(12)
        if (++size > threshold)
            // 超过了就调用resize
            resize();
    	// void afterNodeInsertion(boolean evict) { }
    	// 可以让HashMap的子类去实现
        afterNodeInsertion(evict);
    	// 成功就返回null
    	// 回到put()
        return null;
    }

resize()

final Node<K,V>[] resize() {
    // 将table赋给oldTab这个节点数组
    // 此时table为null或者长度为0
    Node<K,V>[] oldTab = table;
    // 如果oldTab为null时返回0
    // 不为空时返
    // 第一次进入为0
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        // 第一次为0进入这里
        // static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
        newCap = DEFAULT_INITIAL_CAPACITY;
        //  static final float DEFAULT_LOAD_FACTOR = 0.75f;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        // 此时newCap(空间)为16,newThr(临界值)为0.75*16 = 12
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    // 到这里
    // 将newThr(12)赋值给threshold
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
    	// 让newTab指向一个new的Node[16]
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    // 将newTab赋给table
    table = newTab;
    
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    // 返回newTab
    return newTab;
}

第二次add()源码分析

设这次是一个和之前存入的不同的值

由于前面看过一些,直接到putVal()方法

putVal()

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    	// 定义了一些辅助变量:一个Node<K,V>[](节点数组),一个Node<K,V>(节点),两个int值
        Node<K,V>[] tab; Node<K,V> p; int n, i;
    	// transient Node<K,V>[] table;
    	// 第二次add()是,table不为null
    	// 短路或:只执行tab = table
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
    	// 到这里
    	// 执行 p = tab[i = (n - 1) & hash],计算这个值应该存储的位置
    	// 如果这个值不同,hash值也不同,p = tab[i = (n - 1) & hash]是没有元素的
        if ((p = tab[i = (n - 1) & hash]) == null)
            // 创建一个Node放进tab[i]
            // hash = hash(key)  key = "item"	 value = Object对象
            tab[i] = newNode(hash, key, value, null);
    		// 往下看
        else {
            Node<K,V> e; K k;
            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) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
    	// ++修改次数
        ++modCount;
    	// ++size并判断有没有超过临界值(12)
        if (++size > threshold)
            // 超过了就调用resize
            resize();
    	// void afterNodeInsertion(boolean evict) { }
    	// 可以让HashMap的子类去实现
        afterNodeInsertion(evict);
    	// 成功就返回null
    	// 回到put()
        return null;
    }

第二次add()源码分析

设这次是一个和之前存入的相同的值

由于前面看过一些,直接到putVal()方法

putVal()

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    	// 定义了一些辅助变量:一个Node<K,V>[](节点数组),一个Node<K,V>(节点),两个int值
        Node<K,V>[] tab; Node<K,V> p; int n, i;
    	// transient Node<K,V>[] table;
    	// 第二次add()是,table不为null
    	// 短路或:只执行tab = table
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
    	// 到这里
    	// 执行 p = tab[i = (n - 1) & hash],计算这个值应该存储的位置
    	// 值相同情况下,tab[i = (n - 1) & hash]是有值的
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
    	// 进入这里
        else {
            // 定义辅助变量
            Node<K,V> e; K k;
            // 三种情况
            // 1.
            // p = tab[i = (n - 1) & hash]
            // 用tab[i = (n - 1) & hash]的hash值和hash比较
            // 也就是比较(计算下来key应该存储的位置的第一个值(存储的是一个链表)).hash属性,
            // 就是上次的存储的链表的第一个值的hash属性
            // 也就是hash(之前的key) 和 hash(key)比较
            // 也就是比较当前计算下来的索引位置(是个链表)的第一个元素和准备新添加的元素的hash是否相等
            if (p.hash == hash && //如果相等就已经成立了,第二个判断不执行,如果是对象则需要进行下一步equals判断
                ((k = p.key) == key || (key != null && key.equals(k))))
                // 将p赋给e
                // 此时e为tab[i = (n - 1) & hash],也就是当前计算下来的索引位置(是个链表)的第一个元素
                e = p;
            // 2.
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            // 3.
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            // 当前计算下来的索引位置(是个链表)的第一个元素不为空是进去
            // 成立
            if (e != null) { // existing mapping for key
                // 将当前计算下来的索引位置(是个链表)的第一个元素的值赋给oldValue
                V oldValue = e.value;
                // put方法中:putVal(hash(key), key, value, false, true);
                // !false为true,能进去
                if (!onlyIfAbsent || oldValue == null)
                    // 将新值添加进去替换
                    e.value = value;
                // 可以让HashMap的子类去实现
                afterNodeAccess(e);
                // 返回oldValue,也就是添加过了的相同的值
                // 不返回null,也就是说在add()返回false
                // 添加失败
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
posted @ 2022-02-26 13:08  CoderCatIce  阅读(26)  评论(0编辑  收藏  举报