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的主要内容,如果未能解决你的问题,请参考以下文章
一篇文章彻底读懂HashMap之HashMap源码解析(上_)