JavaSE_坚持读源码_HashMap对象_get_Java1.7

Posted rocker-pg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE_坚持读源码_HashMap对象_get_Java1.7相关的知识,希望对你有一定的参考价值。

当你从HashMap里面get时,你其实在干什么?

 1 /**
 2      * Returns the value to which the specified key is mapped,
 3      * or {@code null} if this map contains no mapping for the key.
 4      *
 5      * <p>More formally, if this map contains a mapping from a key
 6      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
 7      * key.equals(k))}, then this method returns {@code v}; otherwise
 8      * it returns {@code null}.  (There can be at most one such mapping.)
 9      *
10      * <p>A return value of {@code null} does not <i>necessarily</i>
11      * indicate that the map contains no mapping for the key; it‘s also
12      * possible that the map explicitly maps the key to {@code null}.
13      * The {@link #containsKey containsKey} operation may be used to
14      * distinguish these two cases.
15      *
16      * @see #put(Object, Object)
17      */
18     public V get(Object key) {
19       //如果key是null,调用getForNullKey,这个方法会去获取table[0]元素
20         if (key == null)
21             return getForNullKey();
22       //否则根据key调用getEntry方法
23         Entry<K,V> entry = getEntry(key);
24 
25         return null == entry ? null : entry.getValue();
26     }

 

 1 /**
 2      * Returns the entry associated with the specified key in the
 3      * HashMap.  Returns null if the HashMap contains no mapping
 4      * for the key.
 5      */
 6     final Entry<K,V> getEntry(Object key) {
 7     //这个size是在当前map中的key-value的映射数量
 8         if (size == 0) {
 9             return null;
10         }
11     
12     //这里又对key进行了下非空校验,如果不为空,通过hash()获取key对应的hash值
13         int hash = (key == null) ? 0 : hash(key);
14     //取出table指定索引处的值,如果hash和key都能匹配上
15         for (Entry<K,V> e = table[indexFor(hash, table.length)];
16              e != null;
17              e = e.next) {
18             Object k;
19             if (e.hash == hash &&
20                 ((k = e.key) == key || (key != null && key.equals(k))))
21                 return e;
22         }
23         return null;
24     }

 

从上面代码中可以看出,如果 HashMap 的每个 bucket 里只有一个 Entry 时,HashMap 可以根据索引、快速地取出该 bucket 里的 Entry;在发生“Hash 冲突”的情况下,单个 bucket 里存储的不是一个 Entry,而是一个 Entry 链,系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那系统必须循环到最后才能找到该元素。

归纳起来简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据 Hash 算法来决定其存储位置;当需要取出一个 Entry 时,也会根据 Hash 算法找到其存储位置,直接取出该 Entry。由此可见:HashMap 之所以能快速存、取它所包含的 Entry,完全类似于现实生活中母亲从小教我们的:不同的东西要放在不同的位置,需要时才能快速找到它。

 

//-----------------------负载因子(loadfactor)我也没搞懂,有待继续研究------------------------------

当创建 HashMap 时,有一个默认的负载因子(load factor),其默认值为 0.75,这是时间和空间成本上一种折衷:增大负载因子可以减少 Hash 表(就是那个 Entry 数组)所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的的操作(HashMap 的 get() 与 put() 方法都要用到查询);减小负载因子会提高数据查询的性能,但会增加 Hash 表所占用的内存空间。

掌握了上面知识之后,我们可以在创建 HashMap 时根据实际需要适当地调整 load factor 的值;如果程序比较关心空间开销、内存比较紧张,可以适当地增加负载因子;如果程序比较关心时间开销,内存比较宽裕则可以适当的减少负载因子。通常情况下,程序员无需改变负载因子的值。

//-----------------------负载因子(loadfactor)我也没搞懂,有待继续研究------------------------------

 

如果开始就知道 HashMap 会保存多个 key-value 对,可以在创建时就使用较大的初始化容量,如果 HashMap 中 Entry 的数量一直不会超过极限容量(capacity * load factor),HashMap 就无需调用 resize() 方法重新分配 table 数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为 capacity 的 Entry 数组),因此创建 HashMap 时初始化容量设置也需要小心对待。

以上是关于JavaSE_坚持读源码_HashMap对象_get_Java1.7的主要内容,如果未能解决你的问题,请参考以下文章

JavaSE_坚持读源码_Class对象_Java1.7

一篇文章彻底读懂HashMap之HashMap源码解析(上_)

21_JavaSE_HashSet类和HashMap类

Java源码初学_HashSet&LinkedHashSet

JavaSE_09_Map

JDK源码_浅谈HashMap