• Java集合系列:-----------08HashMap的底层实现


    对于HashMap感觉一直是看了忘,忘了看。这里就单独写一篇日志来记录一下。HashMap的底层实现。

     非常好的讲HashMap的博客:http://blog.csdn.net/vking_wang/article/details/14166593

    我们看一下HashMap的底层结构:

     我们看到底层是一个数组,数组里面存放的是Entry,Enrty是什么?

    我们看一下源代码:

       
    //很明显这是一个单向链表,next是只想下一个Entry的
    //Key就是我们键值
    //value是我们的值
     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;
            }
    
            public final K getKey() {
                return key;
            }
    
            public final V getValue() {
                return value;
            }
    
            public final V setValue(V newValue) {
                V oldValue = value;
                value = newValue;
                return oldValue;
            }
    
            public final boolean equals(Object o) {
                if (!(o instanceof Map.Entry))
                    return false;
                Map.Entry e = (Map.Entry)o;
                Object k1 = getKey();
                Object k2 = e.getKey();
                if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                    Object v1 = getValue();
                    Object v2 = e.getValue();
                    if (v1 == v2 || (v1 != null && v1.equals(v2)))
                        return true;
                }
                return false;
            }
    
            public final int hashCode() {
                return (key==null   ? 0 : key.hashCode()) ^
                       (value==null ? 0 : value.hashCode());
            }
    
            public final String toString() {
                return getKey() + "=" + getValue();
            }
    
            /**
             * This method is invoked whenever the value in an entry is
             * overwritten by an invocation of put(k,v) for a key k that's already
             * in the HashMap.
             */
            void recordAccess(HashMap<K,V> m) {
            }
    
            /**
             * This method is invoked whenever the entry is
             * removed from the table.
             */
            void recordRemoval(HashMap<K,V> m) {
            }
        }

    这个就是数组里面存放的Entry,里面存放了Key键值还有键值对应的value。

    我们思考的问题是,HashMap是怎么把key和value放进去的,我们常用的就是:

    HashMap hashmap=new HashMap();

    hashmap.put(key,value);

    其实就是根据传进来的key的值计算hash值,根据hash值确定要放在数组的哪个位置。比如放在table[2];

    先去table[2]这个位置找Entry,Entry是一个链表,遍历这个链表,发现这个链表中有key的值相等与传进来的key相等那么之前的值就被替换掉,并且把老的值返回。

    对应的源代码是:

      int hash = hash(key.hashCode());
            int i = indexFor(hash, table.length);
            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;
                }
            }

    但是如果我们在table[2]这个位置看到本身就没有Entry的话,我们就要新增一个Entry。

    代码如下:

     我们看一下addEntry的代码:

        void addEntry(int hash, K key, V value, int bucketIndex) {
    //把table数组上面的Entry取出来,
            Entry<K,V> e = table[bucketIndex];
    //新建一个Entry,并把之前取出来的Entry挂在到这个新加入的Entry的next
    //下面
            table[bucketIndex] = new Entry<>(hash, key, value, e);
            if (size++ >= threshold)
                resize(2 * table.length);
        }

    我们再看一下HashMap是怎么取值的:

    hashmap.get(Key):

    代码如下:

     先找到Table数组里面Entry的位置,然后从这个位置里面搜索这个Entry这个单链表,找到key对应的Entry的value。

       

     

    上面三种遍历方法,给出一个例子来讲解:

    import java.util.Map;
    import java.util.Random;
    import java.util.Iterator;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map.Entry;
    import java.util.Collection;
    
    /*
     * @desc 遍历HashMap的测试程序。
     *   (01) 通过entrySet()去遍历key、value,参考实现函数:
     *        iteratorHashMapByEntryset()
     *   (02) 通过keySet()去遍历key、value,参考实现函数:
     *        iteratorHashMapByKeyset()
     *   (03) 通过values()去遍历value,参考实现函数:
     *        iteratorHashMapJustValues()
     *
     * @author skywang
     */
    public class HashMapIteratorTest {
    
        public static void main(String[] args) {
            int val = 0;
            String key = null;
            Integer value = null;
            Random r = new Random();
            HashMap map = new HashMap();
    
            for (int i=0; i<12; i++) {
                // 随机获取一个[0,100)之间的数字
                val = r.nextInt(100);
                
                key = String.valueOf(val);
                value = r.nextInt(5);
                // 添加到HashMap中
                map.put(key, value);
                System.out.println(" key:"+key+" value:"+value);
            }
            // 通过entrySet()遍历HashMap的key-value
            iteratorHashMapByEntryset(map) ;
            
            // 通过keySet()遍历HashMap的key-value
            iteratorHashMapByKeyset(map) ;
            
            // 单单遍历HashMap的value
            iteratorHashMapJustValues(map);        
        }
        
        /*
         * 通过entry set遍历HashMap
         * 效率高!
         */
        private static void iteratorHashMapByEntryset(HashMap map) {
            if (map == null)
                return ;
    
            System.out.println("
    iterator HashMap By entryset");
            String key = null;
            Integer integ = null;
            Iterator iter = map.entrySet().iterator();
            while(iter.hasNext()) {
                Map.Entry entry = (Map.Entry)iter.next();
                
                key = (String)entry.getKey();
                integ = (Integer)entry.getValue();
                System.out.println(key+" -- "+integ.intValue());
            }
        }
    
        /*
         * 通过keyset来遍历HashMap
         * 效率低!
         */
        private static void iteratorHashMapByKeyset(HashMap map) {
            if (map == null)
                return ;
    
            System.out.println("
    iterator HashMap By keyset");
            String key = null;
            Integer integ = null;
            Iterator iter = map.keySet().iterator();
            while (iter.hasNext()) {
                key = (String)iter.next();
                integ = (Integer)map.get(key);
                System.out.println(key+" -- "+integ.intValue());
            }
        }
        
    
        /*
         * 遍历HashMap的values
         */
        private static void iteratorHashMapJustValues(HashMap map) {
            if (map == null)
                return ;
            
            Collection c = map.values();
            Iterator iter= c.iterator();
            while (iter.hasNext()) {
                System.out.println(iter.next());
           }
        }
    }

     下面的一篇博客我们就要讲解TreeMap了,TreeMap涉及到红黑树,看完代码面试那本书的二叉树那个章节在来看这方面的内容。

  • 相关阅读:
    「ZJOI2019」开关
    「ZJOI2019」Minimax 搜索
    杨氏矩阵学习笔记
    「LibreOJ NOI Round #2」简单算术
    「LibreOJ NOI Round #2」小球进洞
    组合总和 II(力扣第40题)
    组合总和 I(力扣第39题)
    组合(力扣第77题)
    使用MapReduce解决蚂蚁森林第二题
    Hive练习--蚂蚁森林习题二
  • 原文地址:https://www.cnblogs.com/shenxiaoquan/p/6212740.html
Copyright © 2020-2023  润新知