概述

 

HashMap是Map接口的一个哈希表的实现,内部是一个数组表示的。数组中的元素叫做一个Node,一个Node可以一个是一个简单的表示键值对的二元组,也可以是一个复杂的TreeNode。如果是一个一个简单的二元组,则可以通过Node的next域构成构成一个链表。

  transient Node<K,V>[] table;
 

当需要遍历Map的时候,建议使用entrySet域来进行遍历,而不是keySet。因为entrySet其实返回的是一个特殊的set,这个set并未保存任何元素,而是定义了一个特殊的迭代器,这个迭代器直接遍历map中的table,因此能直接得到一个entry的键和值,也就是说,使用entrySet进行遍历的时候,一次遍历即可。(hash冲突除外)。而使用keyset得到的只是key的set,使用key去获取value的时候,还需要进行一次查找。

 

如果只需要值,那么就调用values方法,此方法返回一个特殊的集合,它保存了一个entryset,并使用entryset的迭代器对table进行访问,因此效率也是很高的。

 

方法详述

  • comparableClassFor
    Class<?> comparableClassFor(Object x)

    获取对象x的可比较的class,即尝试从对象x中找到一个Comparable<x.getClass()> 对象。

    首先检查对象x是否是一个comparable的实例:

    if (x instanceof Comparable)

    如果是的话,获取x的class,顺便检查一下是否是String,因为Stirng是用的最多的,顺便加个速。

    如果不是String类,那么就获取此类直接实现的接口(注意,一定是直接实现的)。

    c.getGenericInterfaces())

    然后判断每个父接口是否是参数化类型,且参数化类型的具体类型为compare:

    if (((t = ts[i]) instanceof ParameterizedType) &&

    ((p = (ParameterizedType)t).getRawType() ==

    Comparable.class)

    如果是的话,取出此参数化类型的实际参数类型,如果确实存在,且参数类型和对象x的类型相同,那么这个父接口就是最终要找的comparable接口了。

    (as = p.getActualTypeArguments()) != null &&

    as.length == 1 && as[0] == c) //

  • tableSizeFor

          int tableSizeFor(int cap) int n = cap - 1; n |= n >>> 1; n |= n
    >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>>
    16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY
    : n + 1;

    这段代码非常巧妙,通过位移和或运算计算出比cap大的最小的2的N次方的值。
    第一行:cap-1是为了解决cap本来就是2的N次方的问题。

    然后进行一次右移,一次右移的效果是:如果某一为本来是1,那么一次位移并或之后,它的低一位也会变成1.这样就将一个1变为2个1了。如果本来是0,那么还是0.

    第一步已经将一个1变为2个1了,那么第二步右移两位,就能将两个1变为4个1,接着变为8个1,16个1,最多不超过32个1.

    这个数字最终的效果是,从低往高看,后面的位数上全是1,然后这个数加1,就是原始的cap的2的N次方最小值了。

  • putVal

    第一步:找到带加入的key所在的桶,使用的办法是用桶数组的大小减1然后和hey的哈希值进行与,这其实是让key的哈希值对桶的大小取余的简便操作,提升了效率。

    第二步:如果桶中当前没有元素,则加入,完成。如果桶中已经有值了,说明发生了hash冲突,此时需要将新加入的值沿着桶中已有的值往下排,构成一个链表。

    在第二中,如果一个桶中的元素超过了一个阀值,默认为8,则不再使用链表来保存桶中的元素,而是改成用二叉查找树来保存,以提高检索效率。

    如果桶中的元素当前已经是一个二叉查找树了,那么就加新元素加入到树中。

    加入一个新元素之后,需要检查Map的数量是否满足填装因子的限制,如果不满足了,需要进行重新hash。

最新文章

  1. SQL SQL语句的增删改查
  2. JS验证身份证号码合法性
  3. Excel替换应用
  4. codeforces 446C DZY Loves Fibonacci Numbers 线段树
  5. UPDATE语句:将一个表里的字段更新到另一个表的字段里的语句
  6. Java 数据结构之ArrayList
  7. Android手势解锁, 九宫格解锁
  8. hdu1258Sum It Up (DFS)
  9. Kickstart Practice Round 2017 Google
  10. Python 操作 Azure Blob Storage
  11. 【Alpha】——Fourth Scrum Meeting
  12. 从vultr购买到搭ss看世界
  13. JS 中 原生方法 (三) --- Date 日期
  14. sublime前端必备插件
  15. vue项目中跳转到外部链接方法
  16. git下载、安装、连接github
  17. uva-141-枚举
  18. iOS开发-ViewController的生命周期和切换
  19. V-rep学习笔记:机器人模型创建2—添加关节
  20. 列表中不限制宽度,hover时,字体font-weight:bold,防止抖动

热门文章

  1. https加密
  2. Nagios配置—添加linux主机监控
  3. CentOS 7 之几个新特性(转)
  4. PHP获取文件扩展名的多种方法
  5. js中将函数传递给另一个函数的解析(非常容易理解)
  6. z-index的最大值、最小值
  7. Laravel 依赖注入原理
  8. target vs currentTarget, clientWidth vs offsetWidth
  9. uva10003 Cutting Sticks
  10. delphi根据进程PID获取程序所在路径的函数(用OpenProcess取得句柄,用GetModuleFileNameEx取得程序名)