HashMap部分源码阅读,主要是put方法相关的部分

前言:一定要理解是有顺序的很多桶,桶中装的可不是一个元素。桶的数量就是hashmap通常所说的容量(单位是桶)。桶的数量不一定等于数量size(),so很明显容量不是存放的元素个数。
源码中显示的hashmap的容量就是底层table数组的长度
1、初始桶数量:
int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
2、最大的桶数量:
MUST be a power of two <= 1<<30
int MAXIMUM_CAPACITY = 1 << 30;  // 最大的power of two
3、load factor:负载因子
float DEFAULT_LOAD_FACTOR = 0.75f;
4、临界值
threshold  // 第一次进来的时候存的就是 初始化桶数量(然后在第一次put的时候,table是空的,然后才进扩容的方法
inflateTable(threshold)进行扩容table同时,修改这个临界值=capacity * loadFactor
5、HashMap的put方法(体会hash取模后,生成链表的过程)
public V put(K key, V value) {
        if (table == EMPTY_TABLE) { // 映射数组是空的
            inflateTable(threshold); // 初始化table数组(底层的table其实初始化的时候还是0,第一次put时候才在此方法中扩容table。也有道理确实没必要上来就初始化个table[innitCapital],在你要用的时候再扩大)

// 扩容的方法中 Find a power of 2 >= toSize // 如果传入的桶数量不是2的倍数,那么算出离它最近的且比它大的power of two
        int capacity = roundUpToPowerOf2(toSize);

        }
        if (key == null)
            return putForNullKey(value); // put到key为null的v中,且null映射在在table[0]中     
        int hash = hash(key); // 哈希值
        int i = indexFor(hash, table.length); // (hash & table.length-1) 与运算的取模(对length取模),得到table映射数的下标

K % 2的n次方 = K & (2的n次方 - 1)  :此算法只适合 幂次方运算,所以hashmap的容量是2的倍数
解释:2的n次方减一含义就是将这个2进制数000011111111111111111这种类型的数,这样的话1的个数正好是要取模的位数(比如取后三位),然后与运算就拿到了模值。其二呢,好处是,如果hash值是负数,取模不存在的,还是正数
注:int最大值(2^31-1)所以,所以hashmap最大容量就是(2^30)。

        for (Entry<K,V> e = table[i]; e != null; e = e.next) { // 拿到下标是 i 的映射链表(桶)对象遍历
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // key相同,那么替换返回旧值

此处如果仅仅是hash相同,其他不同,那么就是hash冲突了。

                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue; // 
            }
        }
// 如果不存在key或者是hash冲突了,那么久添加到table[i]下的链表中
        modCount++;

HashMap结构修改的次数,结构性的修改是指,改变Entry的数量

        addEntry(hash, key, value, i); // hash和key相同是不会走到这的,所以hash就算冲突了,他也是在同一个链表中,他们的key'是不同的

 void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) { // 超过桶临界值且table当前处 不是null,扩容且对key再次hash(key)
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex); // 没有超过临界值,就在table当前处创建entry
    }

 void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex]; // 此处拿到当前处的映射
        table[bucketIndex] = new Entry<>(hash, key, value, e); // 同时在创建一个新映射   
        size++;
    }


  static class Entry<K,V> implements Map.Entry<K,V> {
        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n; // 新的entry就next指向了老的(挤到下面),最下面的entry指向null
            key = k;
            hash = h;
        }



        return null;
    }




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章