HashMap原理分析put get remove


HashMap
在面试的时候问原理,我不知道,问我怎么遍历,我也记不清了,但是,我的确经常用。哎!
人家面试官都怀疑,你是不是写过代码了。
所以还是需要探究一下hashmap如何实现的。




static class HashMapEntry<K, V> implements Entry<K, V> {
        final K key;
        V value;
        final int hash;
        HashMapEntry<K, V> next;

        HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;
        }
}
学过数据结构的你,熟悉不熟悉,这就是一个链表的结构,
还有一个变量: transient HashMapEntry<K, V>[] table;
这个维护了一个链表的数组,也就是说,所有的数据最后都会存储到这个table里面,下面我们把hashmap的关键代码粘贴出来,实现自己的map,这样可以一步一步看看HashMap详细的原理过程。
首先把整体代码放出来,再根据使用的时候的代码一步一步去分析
  • -
//MapGac 维护了一个EntryGac的接口,这个接口 主要提供基础的 key value的方法
public interface MapGac<K,V> {
    public static interface EntryGac<K,V>{
           public K getKey();

            /**
             * Returns the value.
             *
             * @return the value
             */
            public V getValue();

            /**
             * Returns an integer hash code for the receiver. {@code Object} which are
             * equal return the same value for this method.
             *
             * @return the receiver's hash code.
             * @see #equals(Object)
             */
            public int hashCode();

            /**
             * Sets the value of this entry to the specified value, replacing any
             * existing value.
             *
             * @param object
             *            the new value to set.
             * @return object the replaced value of this entry.
             */
            public V setValue(V object);


    };
}

下面是HashMapGac
下面是HashMapGac
public class HashMapGac <K, V>{
    HashMapEntryGac<K, V> entryForNullKey;//当key 为null时候 使用这个变量
     /**
     * hashmap元素的个数
     */
    transient int size;
    private static final int MINIMUM_CAPACITY = 4;
    private static final int MAXIMUM_CAPACITY = 1 << 30;
    /**
     * The table is rehashed when its size exceeds this threshold.
     * The value of this field is generally .75 * capacity, except when
     * the capacity is zero, as described in the EMPTY_TABLE declaration
     * above.
     */
    private transient int threshold;
    /**
     * Incremented by "structural modifications" to allow (best effort)
     * detection of concurrent modification.
     */
    transient int modCount;
    transient HashMapEntryGac<K, V>[] table;
    private static final MapGac.EntryGac[] EMPTY_TABLE
    = new HashMapEntryGac[MINIMUM_CAPACITY >>> 1];

    public HashMapGac() {
        table = (HashMapEntryGac<K, V>[]) EMPTY_TABLE;
        threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
    }
    //HashMap里面维护一个链表结构 HashMapEntry
    static class HashMapEntryGac<K, V> implements MapGac.EntryGac<K, V> {
        final K key;
        V value;
        final int hash;
        HashMapEntryGac<K, V> next;

        HashMapEntryGac(K key, V value, int hash, HashMapEntryGac<K, V> next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        @Override
        public final boolean equals(Object o) {
            if (!(o instanceof MapGac.EntryGac)) {
                return false;
            }
            MapGac.EntryGac<?, ?> e = (MapGac.EntryGac<?, ?>) o;
            return Objects.equals(e.getKey(), key)
                    && Objects.equals(e.getValue(), value);
        }

        @Override
        public final int hashCode() {
            return (key == null ? 0 : key.hashCode()) ^
                    (value == null ? 0 : value.hashCode());
        }

        @Override 
        public final String toString() {
            return key + "=" + value;
        }
    }

     private static int secondaryHash(int h) {
            // Spread bits to regularize both segment and index locations,
            // using variant of single-word Wang/Jenkins hash.
            h += (h <<  15) ^ 0xffffcd7d;
            h ^= (h >>> 10);
            h += (h <<   3);
            h ^= (h >>>  6);
            h += (h <<   2) + (h << 14);
            return h ^ (h >>> 16);
     }
     public V get(Object key) {
            Log.gac("getMethod");
            if (key == null) {
                HashMapEntryGac<K, V> e = entryForNullKey;
                return e == null ? null : e.value;
            }

            int hash =secondaryHash(key.hashCode());
            HashMapEntryGac<K, V>[] tab = table;

            for (HashMapEntryGac<K, V> e = tab[hash & (tab.length - 1)];
                    e != null; e = e.next) {
                Log.gac("get index:"+(e.hash & (tab.length - 1))+" key:"+e.key);
                K eKey = e.key;
                if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                    return e.value;
                }
            }
            return null;
        }
     public V put(K key, V value) {
           if (key == null) {
               return putValueForNullKey(value);
           }
            Log.gac("putmehtod");
            int hash = secondaryHash(key.hashCode());
           // Log.gac("hash:"+hash);
            HashMapEntryGac<K, V>[] tab = table;
            //Log.gac("length:"+(tab.length -1));
            int index = hash & (tab.length - 1);
            //Log.gac("index:"+index);
            //如果hash 和key存在 map中则修改它的值
            for (HashMapEntryGac<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++;
           // Log.gac("modCount:"+modCount+" threshold:"+threshold);
            if (size++ > threshold) {
                tab = doubleCapacity();
                index = hash & (tab.length - 1);
            }
            addNewEntry(key, value, hash, index);
            return null;
        }

        public V remove(Object key) {
            if (key == null) {
                return removeNullKey();
            }
            int hash = secondaryHash(key.hashCode());
            HashMapEntryGac<K, V>[] tab = table;
            int index = hash & (tab.length - 1);
            for (HashMapEntryGac<K, V> e = tab[index], prev = null;
                    e != null; prev = e, e = e.next) {
                if (e.hash == hash && key.equals(e.key)) {
                    if (prev == null) {
                        tab[index] = e.next;
                    } else {
                        prev.next = e.next;
                    }
                    modCount++;
                    size--;
                    //postRemove(e);
                    return e.value;
                }
            }
            return null;
        }
          private V removeNullKey() {
                HashMapEntryGac<K, V> e = entryForNullKey;
                if (e == null) {
                    return null;
                }
                entryForNullKey = null;
                modCount++;
                size--;
                //postRemove(e);
                return e.value;
            }
        void addNewEntry(K key, V value, int hash, int index) {
            Log.gac("addNewEntry===index:"+index+" key:"+key+ " value:"+value);
            if(table[index] != null)
             Log.gac(table[index].key+ " "+table[index].value );
            //头部插入构造单链表
            //插入一个元素则作为链表的表头 先前的表头table[index] 作为当前表头的下一个元素
            table[index] = new HashMapEntryGac<K, V>(key, value, hash, table[index]);

        }

        private V putValueForNullKey(V value) {
            HashMapEntryGac<K, V> entry = entryForNullKey;
            if (entry == null) {
                addNewEntryForNullKey(value);
                size++;
                modCount++;
                return null;
            } else {
                preModify(entry);
                V oldValue = entry.value;
                entry.value = value;
                return oldValue;
            }
       }

     void preModify(HashMapEntryGac<K, V> e) { }
     //可以为空值的时候 调用它
     void addNewEntryForNullKey(V value) {
            entryForNullKey = new HashMapEntryGac<K, V>(null, value, 0, null);
      }

     private HashMapEntryGac<K, V>[] doubleCapacity() {
            Log.gac("doubleCapacity");
            HashMapEntryGac<K, V>[] oldTable = table;
            int oldCapacity = oldTable.length;
            if (oldCapacity == MAXIMUM_CAPACITY) {
                return oldTable;
            }
            //Log.gac("oldCapacity:"+oldCapacity);
            int newCapacity = oldCapacity * 2;
            HashMapEntryGac<K, V>[] newTable = makeTable(newCapacity);
            //Log.gac("size:"+size);
            if (size == 0) {
                return newTable;
            }

            for (int j = 0; j < oldCapacity; j++) {
                /*
                 * Rehash the bucket using the minimum number of field writes.
                 * This is the most subtle and delicate code in the class.
                 */
                HashMapEntryGac<K, V> e = oldTable[j];
                if (e == null) {
                    continue;
                }
                int highBit = e.hash & oldCapacity;
                //Log.gac("hithBit:"+highBit);
                HashMapEntryGac<K, V> broken = null;
                Log.gac("j|hightBit:"+(j|highBit));
                newTable[j | highBit] = e;
                Log.gac("key1:"+e.key+" value1:"+e.value);
                for (HashMapEntryGac<K, V> n = e.next; n != null; e = n, n = n.next) {
                    int nextHighBit = n.hash & oldCapacity;
                    //Log.gac("nextHighBit:"+nextHighBit);
                    //Log.gac("hithBit:"+highBit+" key:"+n.key+" value:"+n.value);
                    if (nextHighBit != highBit) {
                        if (broken == null){
                            newTable[j | nextHighBit] = n;
                            //Log.gac("j|nexHighBit:"+(j | nextHighBit));
                        }
                        else{
                            broken.next = n;
                        }
                        broken = e;
                        //Log.gac("key:"+e.key+" value:"+e.value);
                        highBit = nextHighBit;
                    }
                }
                if (broken != null)
                    broken.next = null;
            }
            return newTable;
        }

        //为hashtab 开辟一个新的空间,threshold  为四分之三的newCapacity
        private HashMapEntryGac<K, V>[] makeTable(int newCapacity) {
            Log.gac("makeTable");
            @SuppressWarnings("unchecked") HashMapEntryGac<K, V>[] newTable
                    = (HashMapEntryGac<K, V>[]) new HashMapEntryGac[newCapacity];
            table = newTable;
            threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
            Log.gac("newCapacity:"+newCapacity +" threshold:"+threshold);
            return newTable;
        }
}
下面我们一步一步去分析,首先 初始化 new HashMapGac()
  public HashMapGac() {
        table = (HashMapEntryGac<K, V>[]) EMPTY_TABLE;
        threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
  }
  初始化的时候,table的容量是2 位一个空的HashMapEntryGac数组
  threshold,为总容量的 四分之三的值


下面关键的put方法,如何去存数据呢
  if (key == null) {
    return putValueForNullKey(value);
  }

  如果key 是空值  entryForNullKey = new HashMapEntryGac<K, V>(null, value, 0, null);
  则将这个map 赋值给一个成员变量entryfornullkey,专门存取key为null的mapEntry
  int hash = secondaryHash(key.hashCode());//根据key的hashcode 生成一个hash值
           // Log.gac("hash:"+hash);
  HashMapEntryGac<K, V>[] tab = table;
  int index = hash & (tab.length - 1);//根据hash值 生成table 这个数组链表的索引
            //Log.gac("index:"+index);
 //如果hash 和key存在 map中则修改它的值
  for (HashMapEntryGac<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++;
           // Log.gac("modCount:"+modCount+" threshold:"+threshold);
            if (size++ > threshold) {
                tab = doubleCapacity();
                index = hash & (tab.length - 1);
            }
            addNewEntry(key, value, hash, index);
            return null;

 如果
 size+1 > 4/3 size 则增加tab的容量 开辟双倍空间,
 最后调用addNewEntry方法
  void addNewEntry(K key, V value, int hash, int index) {
            Log.gac("addNewEntry===index:"+index+" key:"+key+ " value:"+value);

            //头部插入构造单链表
            //插入一个元素则作为链表的表头 先前的表头table[index] 作为当前表头的下一个元素
            table[index] = new HashMapEntryGac<K, V>(key, value, hash, table[index]);
}
这个方法需要理解,  table[index] = new HashMapEntryGac<K, V>(key, value, hash, table[index]);
将现有的元素生成一个新的链表的头部,赋值给table[index],最后一个参数,table[index]则为元素的上一个链表的头部
如果不为空,则作为,新元素的头部的next,
采用的是链表头部插入元素的方法.
下面分析doubleCapacity()扩展容量,初始化的时候容量是2,添加了第一个元素,添加第二个元素的时候,就变为4
添加低三个元素,开始添加第四个元素的时候,则变成了8,以此类推。

            HashMapEntryGac<K, V>[] oldTable = table;
            int oldCapacity = oldTable.length;
        //如果容量 已经为最大值,则返回原来的容量值,不在增加了。
            if (oldCapacity == MAXIMUM_CAPACITY) {
                return oldTable;
            }
            //table数组的容量值扩大2倍
            int newCapacity = oldCapacity * 2;

            HashMapEntryGac<K, V>[] newTable = makeTable(newCapacity);

        makeTable方法的如下 :
        HashMapEntryGac<K, V>[] newTable
                    = (HashMapEntryGac<K, V>[]) new HashMapEntryGac[newCapacity];
            table = newTable;
            threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
            new 一个新的table数组,并且赋值给table,threshold 为newCapacity的四分之三




        将oldTable 元素的链表头 以此赋给新的table数组 
            for (int j = 0; j < oldCapacity; j++) {
                /*
                 * Rehash the bucket using the minimum number of field writes.
                 * This is the most subtle and delicate code in the class.
                 */
                HashMapEntryGac<K, V> e = oldTable[j];
                if (e == null) {
                    continue;
                }
                int highBit = e.hash & oldCapacity;

                HashMapEntryGac<K, V> broken = null;
                Log.gac("j|hightBit:"+(j|highBit));
                newTable[j | highBit] = e;
                Log.gac("key1:"+e.key+" value1:"+e.value);
            //如果newTable[j | highBit] 里链表长度 > 1重新构造新的链表 
                for (HashMapEntryGac<K, V> n = e.next; n != null; e = n, n = n.next) {
                    int nextHighBit = n.hash & oldCapacity;
                    //Log.gac("nextHighBit:"+nextHighBit);
                    //Log.gac("hithBit:"+highBit+" key:"+n.key+" value:"+n.value);
                    if (nextHighBit != highBit) {
                        if (broken == null){
                            newTable[j | nextHighBit] = n;
                            //Log.gac("j|nexHighBit:"+(j | nextHighBit));
                        }
                        else{
                            broken.next = n;
                        }
                        broken = e;
                        //Log.gac("key:"+e.key+" value:"+e.value);
                        highBit = nextHighBit;
                    }
                }
                if (broken != null)
                    broken.next = null;
            }
            return newTable;
        }

上面就是整个table[] 数组链表的构造过程
下面对get方法进行分析:

         //如果key为空 则返回entryFroNullkey字段的map
            if (key == null) {
                HashMapEntryGac<K, V> e = entryForNullKey;
                return e == null ? null : e.value;
            }
        //首先取得key的hash值
            int hash =secondaryHash(key.hashCode());
            HashMapEntryGac<K, V>[] tab = table;
            //由hash & (tab.length-1)获取 table的索引index
        //取得链表的头部Entry e 遍历链表 找到 key 和hash值都相等的元素 返回value
            for (HashMapEntryGac<K, V> e = tab[hash & (tab.length - 1)];
                    e != null; e = e.next) {
                Log.gac("get index:"+(e.hash & (tab.length - 1))+" key:"+e.key);
                K eKey = e.key;
                if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                    return e.value;
                }
            }
            return null;
        }
remove方法如下:
//常规的链表删除操作
public V remove(Object key) {
        //如果key为null 删除entryForNullkey = null
            if (key == null) {
                return removeNullKey();
            }
        //取得key的hash值
            int hash = secondaryHash(key.hashCode());
            HashMapEntryGac<K, V>[] tab = table;
        //找到table 索引
            int index = hash & (tab.length - 1);
        //根据索引获取链表头指针,下面就是删除链表节点的方法,上删除的节点的上一个next指针 指向下一个节点 so easy size--
            for (HashMapEntryGac<K, V> e = tab[index], prev = null;
                    e != null; prev = e, e = e.next) {
                if (e.hash == hash && key.equals(e.key)) {
                    if (prev == null) {
                        tab[index] = e.next;
                    } else {
                        prev.next = e.next;
                    }
                    modCount++;
                    size--;
                    //postRemove(e);
                    return e.value;
                }
            }
            return null;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值