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进制数111...(都是1)。
K                              1101010101101010010010010100101... (红色部分是2的正整数倍)
&
2的n次方 - 1                                                                    111...    (都是1)
计算结果:                                                                      101...    (取出K的红色以外的部分也就是余数,就是我们的目的)
这有啥优点呢?
其一运算快,其二呢,好处是,如果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;
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值