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

 /**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null :
* key.equals(k))}, then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
*
* <p>A return value of {@code null} does not <i>necessarily</i>
* indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to {@code null}.
* The {@link #containsKey containsKey} operation may be used to
* distinguish these two cases.
*
* @see #put(Object, Object)
*/
public V get(Object key) {
  //如果key是null,调用getForNullKey,这个方法会去获取table[0]元素
if (key == null)
return getForNullKey();
  //否则根据key调用getEntry方法
Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue();
}
 /**
* Returns the entry associated with the specified key in the
* HashMap. Returns null if the HashMap contains no mapping
* for the key.
*/
final Entry<K,V> getEntry(Object key) {
//这个size是在当前map中的key-value的映射数量
if (size == 0) {
return null;
} //这里又对key进行了下非空校验,如果不为空,通过hash()获取key对应的hash值
int hash = (key == null) ? 0 : hash(key);
//取出table指定索引处的值,如果hash和key都能匹配上
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 != null && key.equals(k))))
return e;
}
return null;
}

从上面代码中可以看出,如果 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的更多相关文章

  1. JavaSE_坚持读源码_HashMap对象_put_Java1.7

    当你往HashMap里面put时,你其实在干什么? /** * Associates the specified value with the specified key in this map. * ...

  2. JavaSE_坚持读源码_ClassLoader对象_Java1.7

    ClassLoader java.lang public abstract class ClassLoader extends Object //类加载器的责任就是加载类,说了跟没说一样 A clas ...

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

    Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识.这项信息纪录了每个对象所属的类.虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类 ...

  4. JavaSE_坚持读源码_ArrayList对象_Java1.7

    底层的数组对象 /** * The array buffer into which the elements of the ArrayList are stored. * The capacity o ...

  5. JavaSE_坚持读源码_HashSet对象_Java1.7

    对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如 ...

  6. JavaSE_坚持读源码_String对象_Java1.7

    /** * Compares this string to the specified object. The result is {@code * true} if and only if the ...

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

    /** * Returns a hash code value for the object. This method is * supported for the benefit of hash t ...

  8. [一起读源码]走进C#并发队列ConcurrentQueue的内部世界

    决定从这篇文章开始,开一个读源码系列,不限制平台语言或工具,任何自己感兴趣的都会写.前几天碰到一个小问题又读了一遍ConcurrentQueue的源码,那就拿C#中比较常用的并发队列Concurren ...

  9. Java读源码之ReentrantLock(2)

    前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...

随机推荐

  1. 了解AutoCAD对象层次结构 —— 2 ——文档

    再次想象另外一个场景:启动AutoCAD程序后,您新建了两个.dwg文件,也就是说创建了两个文档(Document)对象.将窗口进行层叠,您看到的窗口应该与下图类似: 图 4‑3 如何访问这些文档呢? ...

  2. BZOJ1319Sgu261Discrete Roots——BSGS+exgcd+原根与指标+欧拉定理

    题目描述 给出三个整数p,k,a,其中p为质数,求出所有满足x^k=a (mod p),0<=x<=p-1的x. 输入 三个整数p,k,a. 输出 第一行一个整数,表示符合条件的x的个数. ...

  3. #189 stat(动态规划)

    容易想到固定第一个排列为1~n,算出答案后乘上n!即可,但这样就离正解走远了. 考虑排列dp的一般套路,将数从大到小加入排列,这样每个位置第一次填数时(不管是第一个还是第二个排列)其贡献就确定了. 显 ...

  4. Python 面试题_未完

    基础部分 1. 为什么学习Python 家里有在这个IT圈子里面,也想让我接触这个圈子,然后给我建议学的Python, 然后自己通过百度和向有学过Python的同学了解了Python,Python这门 ...

  5. PHP获取网络图片并保存在本地目录

    PHP获取网络图片并保存在本地目录思路: 代码如下: function file_exists_S3($url) { $state = @file_get_contents($url,0,null,0 ...

  6. ARC 086 E - Smuggling Marbles(dp + 启发式合并)

    题意 Sunke 有一棵 \(N + 1\) 个点的树,其中 \(0\) 为根,每个点上有 \(0\) 或 \(1\) 个石子, Sunke 会不停的进行如下操作直至整棵树没有石子 : 把 \(0\) ...

  7. Android 错误提示: Can't create handler inside thread that has not called Looper.prepare()

    Can't create handler inside thread that has not called Looper.prepare() 将 Handler handler = new Hand ...

  8. Integer’s Power HDU - 3208(容斥原理)

    找出(l,r)内的所有的指数最大的次方和 因为一个数可能可以看成a^b和c^d,所以我需要去重,从后往前枚举幂数,然后找可以整除的部分,把低次幂的数去掉. 然后开n方的部分,先用pow()函数找到最接 ...

  9. 低电平ViL

    低电平 编辑 低电平(Vil)指的是保证逻辑门的输入为低电平时所允许的最大输入低电平,当输入电平低于Vil时,则认为输入电平为低电平.   中文名 低电平 外文名 Vil 主要应用 测量电缆和保护连接 ...

  10. centos7安装mha4mysql

    mysql搭建mha需要用的两个rpm包.(manager包和node包) 下载地址:https://download.csdn.net/download/dajdajdajdaj/10603389 ...