我之见--HashMap原理解析

第一:HashMap的概念 

<span style="font-size:14px;">HashMap 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。</span>

第二:什么是哈希表?

<span style="font-size:14px;"> 散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,这个映射函数叫做散列函数,存放记录的数组叫做散列表 PS:给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。</span>
<span style="font-size:14px;">
</span>

第三:什么是HashCode? 

<span style="font-size:18px;">hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。</span>
<span style="font-size:14px;">
</span>
java Hash Map 就是根据对象的hashCode的值,然后通过散列函数计算出存放的地址(也就是索引值)。但是如果遇到计算出来的值相同的话,这种情况叫做哈希冲突,ps:y = x % H  ,如果 x = object.hashCode(); 则y 计算出来的值 就可以能相同,当然出现社种冲突的时候,我们一般可以有以下方法解决。

  1. 开放地址法
    开放地执法有一个公式:Hi=(H(key)+di) MOD m i=1,2,...,k(k<=m-1)
    其中,m为哈希表的表长。di 是产生冲突的时候的增量序列。如果di值可能为1,2,3,...m-1,称线性探
    测再散列。

    如果di取1,则每次冲突之后,向后移动1个位置.如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2) 
    称二次探测再散列。如果di取值可能为伪随机数列。称伪随机探测再散列
  2. 再哈希法 
    当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。
    比如上面第一次按照姓首字母进行哈希,如果产生冲突可以按照姓字母首字母第二位进行哈希,再冲突,第三位,直到不冲突为止
  3. 链地址法
    将所有关键字为同义词的记录存储在同一线性链表中。
  4. 建立一个公共溢出区
    假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录。
    经过以上方法,基本可以解决掉hash算法冲突的问题。

第四:HashMap源码学习

HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable
因此HashMap是实现Map接口的,同时 实现 Serializable接口,所以可以序列化。每个HashMap内部都有一个HashMapEntry 数级对象。
 
我们来看一下 HashMap的 put方法
 @Override public V put(K key, V value) {
        if (key == null) {
            return putValueForNullKey(value);
        }

        int hash = secondaryHash(key.hashCode());
        HashMapEntry<K, V>[] tab = table;
        int index = hash & (tab.length - 1);
        for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }

        // No entry for (non-null) key is present; create one
        modCount++;
        if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
        }
        addNewEntry(key, value, hash, index);
        return null;
    }
如果Key值 为空,则加入 putValueForNullKey(value);

先计算hash 值 
 int hash = secondaryHash(key.hashCode()); 然后 根据 hash值计算index值 :
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
遍历数组查找是否有已经相同key存在,则更新原来的值。否则添加到数组中。 addNewEntry(key, value, hash, index)
我们再来看一下 get方法 :
    public V get(Object key) {
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }

        // Doug Lea's supplemental secondaryHash function (inlined)
        int hash = key.hashCode();
        hash ^= (hash >>> 20) ^ (hash >>> 12);
        hash ^= (hash >>> 7) ^ (hash >>> 4);

        HashMapEntry<K, V>[] tab = table;
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return e.value;
            }
        }
        return null;
    }
根本是根据hashCode计算index ( hash & (tab.length - 1).取相应的Entry,然后对比key值,返回value







。 。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值