HashMap的工作原理

1.    HashMap概述:

   HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。不保证映射的顺序,特别是它不保证该顺序恒久不变。

 

2.    HashMap的数据结构:

   HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。


   从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。

   数组中存放的键值对:

 static class Entry<K,V> implements Map.Entry<K,V> {

        final K key;

        V value;

        Entry<K,V> next;

        final int hash;

        /**

         * Creates new entry.

         */

        Entry(int h, K k, V v, Entry<K,V> n) {

            value = v;

            next = n;

            key = k;

            hash = h;

        }

    }

               Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表


3. HashMap的存取实现:

       1.存储

public V put(K key, V value) {

        //键值为null,采取特别处理

        if (key == null)

            return putForNullKey(value);

        //计算键值的hash值

        int hash = hash(key.hashCode());

//计算  h & (length-1),获得在数组中的序号

        int i = indexFor(hash, table.length);

//如果该位置上已经有键值对了,遍历这个位置上的链表,如果找到相同的key,则对其value进行更新

        for (Entry<K,V> e = table[i]; e != null; e = e.next) {

            Object k;

            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

                V oldValue = e.value;

                e.value = value;

                e.recordAccess(this);

                return oldValue;

            }

        }

        //该位置上没有存储键值对或者不存在相同的key,在该位置上存储键值对

        modCount++;

        addEntry(hash, key, value, i);

        return null;

    }


void addEntry(int hash, K key, V value, int bucketIndex) {

Entry<K,V> e = table[bucketIndex];

//创建键值对,存储在该位置上,其next指向的原来的键值对

        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

//实际容量超过预定负载后,把长度扩展一倍

//threshold = (int)(capacity * loadFactor) 

//capacity必须为2的幂

//loadFactor默认为0.75,可以在构造函数中制定

        if (size++ >= threshold)

            resize(2 * table.length);

    }


private V putForNullKey(V value) {

        for (Entry<K,V> e = table[0]; e != null; e = e.next) {

            if (e.key == null) {

                V oldValue = e.value;

                e.value = value;

                e.recordAccess(this);

                return oldValue;

            }

        }

        modCount++;

//null键总是放在数组第一位置上的链表中

        addEntry(0, null, value, 0);

        return null;

    }  


   2.获取            
public V get(Object key) {
// 键值为null,采取特别处理
        if (key == null)
            return getForNullKey();
//计算键值的hash值
        int hash = hash(key.hashCode());
        //通过hash,计算出其存储位置的索引,在该位置上遍历链表,取得键值对,返回value
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }
 //在数组的第一个位置上遍历链表,查找key为null的键值对
 private V getForNullKey() {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

4.确定数组index:hashcode % table.length取模

HashMap存取时,都需要计算当前key应该对应Entry[]数组哪个元素,即计算数组下标

   /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
 
按位取并,作用上相当于取模mod或者取余%。
这意味着数组下标相同,并不表示hashCode相同。



                                                                                                                                               
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值