HashMap 1. 7 源码讲解

HashMap 1.7 源码讲解

1、参数讲解

	1)hashSeed:用于键的哈希码计算上,用于减少哈希冲突。通过下面所述的initHashSeedAsNeeded方法来进行初始化
	2)threshold:表示可以存放的最大量,它的值为loadFactory*数组容量,但是存在最大值为1<<30+1【即2^30+1】
	3)loadFactory:负载因子,用于表示元素数量超过数组容量多少时,就会扩容,值在(0,1)之间
	4)modCount:每进行一次集合的元素修改,就会进行加1【但是元素的替换并不算集合元素修改之内】
*

2、方法讲解

1)初始化

1、initHashSeedAsNeeded:用于初始化hashSeed
final boolean initHashSeedAsNeeded(int capacity) {
        //hashSeed为零 就返回false 不为零 就返回true
        boolean currentAltHashing = hashSeed != 0;
        //只要useAltHashing为false 那么hashSeed一定为零   sun.misc.VM.isBooted()判断VM是否已经加载完成
        //如果没有加载完成,那么就肯定没有hashSeed的值,所以直接设置hashSeed值为零即可
        boolean useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;
        if (switching) {
            hashSeed = useAltHashing
                    ? sun.misc.Hashing.randomHashSeed(this)
                    : 0;
        }
        return switching;
    }
2、inflateTable:进行数组的初始化,并且会使用上述方法进行hashSeed的初始化
private void inflateTable(int toSize){
        //寻找比当前数大两倍
        int capacity = roundUpToPowerOf2(toSize);
        //重新设置阈值[是当前容量乘以加载因子]
        threshold = (int)Math.min(capacity*loadFactor,MAXMUN_CAPACITY+1);
        table = new Entry[capacity];
        initHashSeedAsNeeded(capacity);
    }

2)获取元素

3、get方法在调用时会先判断key是否为null,key==null,就会调用getForNullKey方法,否则使用getEntry方法
getForNullKey方法:
private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            //只要key为null就进行返回
            if (e.key == null)
                return e.value;
        }
        return null;
    }
getEntry方法:
public Entry<K,V> getEntry(Object key){
        if(size==0){
            return null;
        }
        int hash = key==null?0:hash(key);
        //找到key存储的对应的entry
        Entry<K,V> entries = table[indexFor(hash,table.length)];
        for(; entries!=null ; entries = entries.next){
            Object k ;
            if(entries.hash==hash&&((k=entries.key)==key || (key!=null&&key.equals(k)))){
                return entries;
            }
        }
        return null;
    }

3)新增元素

3、put:进行元素的添加或者元素替换,key值相同会进行值替换,但是如果进行元素新增,会调用addEntry方法
public V put(K key,V value){
        //如果还没有初始化,就进行初始化
        if(table==EMPTY_TABLE){
            inflateTable(threshold);
        }
        if(key==null){
            return putForNullKey(value);
        }
        int hash = hash(key);
        //判断当前是否具有key是传入的key,如果有就直接进行值替换
        for(Entry<K,V> entry=table[indexFor(hash,table.length)] ; entry!=null ; entry = entry.next){
            Object k;
            if(entry.hash==hash&&((k=entry.key)==key || key.equals(k))){
                V oldValue = entry.value;
                entry.value = value;
                return oldValue;
            }
        }
        //如果不是进行元素替换,肯定会进行修改,所以修改次数加1
        modCount++;
        //进行元素的添加
        addEntry(hash,key,value,indexFor(hash,table.length));
        return null;
    }
4、putForCreate:也是进行元素的新增,但是方法并不对外开放。添加元素也不会进行modCount的自增。同样添加元素时,会使用createEntry方法进行新增
//不会去检查是否超过阈值,直接计算
    private void putForCreate(K key,V value){
        int hash = key==null?0:hash(key);
        int bucketIndex = indexFor(hash,table.length);
        //首先检查是否存在相同的key
        for(Entry<K,V> entry = table[bucketIndex] ; entry!=null ; entry = entry.next){
            Object k;
            if((k = entry.key)==key || (key!=null && key.equals(k))){
                entry.value = value;
                return;
            }
        }
        //也是增加节点
        createEntry(hash,key,value,bucketIndex);
    }

5、addEntry方法:进行元素的增加,同时会判断是否需要进行扩容
createEntry方法:也是进行元素的新增,但是并不会进行扩容的判断
//增加元素,并且判断是否超过阈值
    void addEntry(int hash,K key,V value,int bucketIndex){
        if(size>=threshold && null!=table[bucketIndex]){
            resize(2*table.length);
            hash = (null!=key)?hash(key):0;
            bucketIndex = indexFor(hash,table.length);
        }
        createEntry(hash,key,value,bucketIndex);
    }
    //无需判断是否超过阈值,直接添加元素
    void createEntry(int hash,K key,V value,int bucketIndex){
        Entry<K,V> entry = table[bucketIndex];
        //增加
        table[bucketIndex] = new Entry<>(hash,key,value,entry);
        size++;
    }

4)删除元素

6、//删除元素
    public V remove(Object key){
        Entry<K,V> entry = removeEntryForKey(key);
        return entry==null?null:entry.value;
    }
    //真正进行节点删除的方法
    final Entry<K,V> removeEntryForKey(Object key){
        if(size==0){
            return null;
        }
        int hash = key==null?0:hash(key);
        int bucketIndex = indexFor(hash,table.length);
        Entry<K,V> e = table[bucketIndex];
        Entry<K,V> pre = e;
        while(e!=null){
            Entry<K,V> next = e.next;
            Object k;
            if(next.hash==hash && ((k=e.key)==key || (k!=null&&key.equals(k)))){
                modCount++;
                size--;
                //判断是否第一位就和当前要删除的相等
                if(pre==e){
                    table[bucketIndex] = next;
                }else{
                    pre.next = next;
                }
            }
            pre = e;
            e = next;
        }
        return e;
    }

5)扩容

7、resize:进行数组的扩容
transfer:进行数组扩容之后的调整
//进行扩容
    void resize(int newCapacity){
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if(oldCapacity == MAXMUN_CAPACITY){
            threshold = Integer.MAX_VALUE;
            return;
        }
        Entry[] newTable = new Entry[newCapacity];
        //进行数组调整
        transfer(newTable,initHashSeedAsNeeded(newCapacity));
        table = newTable;
        //进行阈值的重新计算
        threshold = (int)Math.min(newCapacity*loadFactor,MAXMUN_CAPACITY+1);
    }
    //进行扩容之后的老数组与新数组的装填
    void transfer(Entry[] newTable,boolean rehash){
        int newCapacity = newTable.length;
        for(Entry<K,V> entry:table){
            while(entry!=null){
                Entry<K,V> next = entry.next;
                用于判断是否进行hash的重新计算
                if(rehash){
                    entry.hash=null==entry.key?0:hash(entry.key);
                }
                //获取到对应的数组位置
                int i = indexFor(entry.hash,newCapacity);
                entry.next = newTable[i];
                newTable[i] = entry;
                entry.next = next;
            }
        }
    }

总结一下:HashMap的数组并不是在初始化的时候就会进行构建的,而是在需要的时候才会进行构建

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值