HashMap的原理分析(2)get和remove方法的实现

public V get(Object key) {  
        // 如果key为空,用get空key方法处理
       if (key == null)  
           return getForNullKey();  
        // 计算key的hash值
       int hash = hash(key.hashCode());  
       // indexfor算出hash桶(就是table数组)的索引,遍历这个位置的entry链表
       for (Entry<K,V> e = table[indexFor(hash, table.length)];  
            e != null;  
            e = e.next) {  
           Object k;  
                   // 如果entry的hash值等于key的hash值(这里跟entry这个静态内部类的hash属性算法有关,应该就是根据key算的),key也相等,equals返回ture,那么就认为找到了这个entry,返回entry.value
           if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
               return e.value;  
       }  
       return null;  
}  
================================================
final Entry<K,V> removeEntryForKey(Object key) {  
        // 依旧是根据key算hash值再根据hash值和数组长度算出索引值,如果key是null,则索引就是0
        int hash = (key == null) ? 0 : hash(key.hashCode());  
        int i = indexFor(hash, table.length); 
        // prey是该索引位置的链表的第一个节点 
        Entry<K,V> prev = table[i];  
        Entry<K,V> e = prev;  
          // 开始遍历链表
        while (e != null) {  
            Entry<K,V> next = e.next;  
            Object k;  
            // 如果各种都相等,那么就认为找到了给定的key对应的entry
            if (e.hash == hash &&  
                ((k = e.key) == key || (key != null && key.equals(k)))) {  
                // 结构变化指标modCount加1,key-value数量size减1
                modCount++;  
                size--;  
                if (prev == e)  
                    table[i] = next;  
                // 将上一个节点prev的next指向当前节点的next,即跳过了当前节点
                // 此时当前节点没有任何引用指向,它在程序结束之后就会被gc回收
                else  
                    prev.next = next;  
                e.recordRemoval(this);  
                return e;  
            }  
            // prev在遍历的尾部被赋值当前节点,也就是在下一次遍历中它代表上一个节点
            // 主要是为了删除
            prev = e;  
            e = next;  
        }  

        return e;  
}  

这三个方法基本是hashmap的核心方法,可以看出hashmap主要是基于一个entry[],entry本身是一个链表,也就是说这个结构就是一个链表数组(在1.8中,为了避免链表过长,默认是8的长度链表时,构造红黑树替代链表结构),数组的索引用key的hashcode,在加上hash算法,再和(table.length-1)作按位与运算得出,这个大的hash算法尽量避免了hash碰撞,但是仍旧会出现key值不同,但是索引相同的情况,这时就依次往该索引位置的链表中放入新的entry,值得注意的是,这个放入过程是从前面插入,newentry.next = oldentry,一目了然。
在前面一篇对put方法进行分析的时候,发现当size大于threshold(阈值)时,初始的table长度为16,threshold=length*loaderfactor(负载因子默认0.75) =12,table会扩容为原来的两倍,直到达到数组的最大长度1<<30(2的30次方),如果size大于这个值,那么就直接修改为Integer.MAX_VALUE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值