在刷leetcode的算法题时,HashMap需要大量使用,而且也是面试的高频问题。这里记录了HashMap一些增、删、改、查的实现细节和时间复杂度,罗列了一些比较有用的方法,以及其它的一些细节。

1、底层数据结构
       HashMap在jdk1.7及之前的版本中,由数组+链表的结构实现,从jdk1.8开始,由数组+链表+红黑树的结构实现,这里在jdk1.8的基础上探讨HashMap。
源码中维护了一个数组:

1 transient Node<K,V>[] table;
2 static class Node<K,V> implements Map.Entry<K,V> {
3 final int hash;
4 final K key;
5 V value;
6 Node<K,V> next;
7 }

这个数组存储的Node,就包含了我们put时的K与V,K的hash值,以及指向下一个节点的指针next。数组中查询节点的时间复杂度是O(1),但是插入、删除的时间
复杂度是O(n),所以执行插入和删除操作比较耗时。HashMap中加入链表结构来解决这个问题。我们知道,解决hash冲突的一般方法有:开发地址法、二次hash法、
拉链法等,这里采用的就是拉链法,也就是这里的数组+链表结构了。查找元素时,最好的情况是就在数组中,时间复杂度为O(1),最坏的情况是在链表的末尾,
时间复杂度是O(n)(当然,由于HashMap的扩容机制和良好的hash算法,hash冲突发生得比较少);插入和删除的时间复杂度就变成了O(1)了。
        jdk1.8加入了红黑树,当链表的长度达到8的时候就会由链表升维为红黑树,当红黑树减少到6时又由红黑树降到链表。这里需要补充一点的是,红黑树的节点占用
的空间比链表要大,维护红黑树的空间成本比较大,但操作方便;而链表正好相反,所以这里的8和6是一个平衡的值。在链表转为红黑树时,还会判断当前的Entry
的数量是否小于64,小于64时会扩容,减少hash冲突,生成红黑树的可能性就小了很多。可见,只有当数量比较多时,维护红黑树的效率才比较明显。
       红黑树的节点如下,实际上也Node的子类:

1 static final class TreeNode<K,V> extends LinkedHashMap.LinkedHashMapEntry<K,V> {
2 TreeNode<K,V> parent; // red-black tree links
3 TreeNode<K,V> left;
4 TreeNode<K,V> right;
5 TreeNode<K,V> prev; // needed to unlink next upon deletion
6 boolean red;
7 }

2、构造函数的选择
      HashMap提供了4个构造函数,实际工作中可能会用到下面3个:

 1 public HashMap() {
2 this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
3 }
4 public HashMap(int initialCapacity) {
5 this(initialCapacity, DEFAULT_LOAD_FACTOR);
6 }
7 public HashMap(Map<? extends K, ? extends V> m) {
8 this.loadFactor = DEFAULT_LOAD_FACTOR;
9 putMapEntries(m, false);
10 }

这三个构造函数都使用了默认的扩容因子,

static final float DEFAULT_LOAD_FACTOR = 0.75f;

其值为0.75,当HashMap当前使用率达到整个容量(capacity)的75%时就会扩容。第一个构造函数使用得最频繁,会分配默认大小的容量:

1 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

第二个构造函数会指定初始容量,指定容量后通过计算,会分配比该初始值大的最近的2的n次方大小的容量,比如传入的initialCapacity为12,实际上会分配16的容量,最大能分配的容量为;

1 static final int MAXIMUM_CAPACITY = 1 << 30;

第三个可以用于复制指定的HashMap。由于扩容需要执行不少操作,所以肯定是会占用一些资源的,如果平时开发比较明确需要使用多少容量,最好使用第二个,可以避免频繁扩容影响性能。

3、元素的插入
      插入元素的方法是put(K,V),其基本步骤是:
  (1)根据Key算出hash值,(n-1)&hash来确定其在数组中的index(这里的n表示数组的长度)
  (2)如果数组的这个index位置为空,则直接插入,时间复杂度是O(1),如果达到扩容条件还会扩容。
  (3)如果数组的这个index已经有值了,那就依次遍历,比价Key来判断是否已经存在,存在就修改该节点的Value,不存在就新建节点并插在链尾。
       如果链表长度达到了8,此时会升维形成红黑树。如果还在链表阶段,时间复杂度是O(1)+O(k),这里O(1)是插入,O(k)是遍历,由于不会超过8,所以也可以认为是O(1)。在形成红黑树时,还会判断容量是否小于64,如果是,会扩容。

(4)在第3步中,可能插入前已经是红黑树了,那就在红黑树中先查找是否存在,存在则修改,不存在则新建并插入。这样,时间复杂度是O(l)+O(logK)。
所以综合来看,可以理解为插入一个元素时时间复杂度最好是O(1),最坏是O(logn)

4、获取元素
     获取元素的方法是get(K),基本步骤是:
  (1)根据Key的hash值确定其在数组中的index。
  (2)先判断数组的这个地方是否有节点,没有则返回null。
  (3)如果有,则根据hash和Key判断第一个节点是否为目标节点,是则返回其Value。否则继续判断,根据第一个节点是TreeNode实例来判断当前是链表还是红黑树。 同样根据hash值和Key来确定是否存在,存在则返回Value,否则返回null。所以时间复杂度也和插入时类似,最好时是O(1),最坏时是O(logn)。


5、删除元素
       删除元素的方法是remove(K),先和获取元素一样查找该节点,删除,然后调整结构。

6、Key为null时的处理
      HashMap的K和V均可以为null,当Key为null时有,其hash值定为0;

1 public V put(K key, V value) {
2 return putVal(hash(key), key, value, false, true);
3 }
4 static final int hash(Object key) {
5 int h;
6 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
7 }

7、做算法题时常用的方法

 1 Map<Object, Object> map = new HashMap<>();
2 map.put(K,V); //存取KV对
3 map.get(K); //如果不存在,则返回null
4 map.getOrDefault(K,defaultValue); //相比get方法,会得到设定的默认值defaultValue。该方法很有用
5 map.entrySet(); //获取所有KV对的实体Set,其元素类型为Map.Entry<K, V>。HashMap中的Node,TreeNode都是其子类。
6 map.keySet(); //获取Key的集合Set
7 map.values(); //获取value的集合Collection,区别于Set
8 map.containsKey(K); //判断是否包含指定Key的Entry
9 map.containsValue(V); //判断是否包含指定Value的Entry
10 map.remove(K); //删除指定Key的Entry
11 map.putAll(otherMap); //复制给定的map
12 map.size(); //Entry的数量
13 map.clear(); //清除所有Entry
14 map.isEmpty(); //判断是否为空

.

【算法】HashMap相关要点记录的更多相关文章

  1. HashMap相关类:Hashtable、LinkHashMap、TreeMap

    前言 很高兴遇见你~ 在 深入剖析HashMap 文章中我从散列表的角度解析了HashMap,在 深入解析ConcurrentHashMap:感受并发编程智慧 解析了ConcurrentHashMap ...

  2. iBatis & myBatis & Hibernate 要点记录

    iBatis & myBatis & Hibernate 要点记录 这三个是当前常用三大持久层框架,对其各自要点简要记录,并对其异同点进行简单比较. 1. iBatis iBatis主 ...

  3. JAVA 中LinkedHashMap要点记录

    JAVA 中LinkedHashMap要点记录 构造函数中可能出现的几个参数说明如下: 1.initialCapacity 初始容量大小,使用无参构造方法时,此值默认是16 2.loadFactor ...

  4. Spring事物管理--相关要点及配置事物管理器

    事务的四大特征 1.原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做要么全不做 2.一致性:数据不会因为事务的执行而遭到破坏 3.隔离性:一个事物的执行,不受其他事务的干扰,即并 ...

  5. 一张思维导图带你梳理HashMap相关知识

    HashMap可以说是java中最常见也是最重要的key-value存储结构类,很多程序员可能经常用,但是不一定清楚这个类背后的数据结构和相关操作原理,为了复习HashMap相关的知识,今天花了一天的 ...

  6. Echarts的相关问题记录与应用

    一.相关问题记录: 1.对图表的div进行隐藏操作,使用hide()或display:none,重新展示时,会造成图表无法获取高度,导致图表的高宽不符合预期: 解决方法:最后调用一下resize()函 ...

  7. JavaScript算法与数据结构知识点记录

    JavaScript算法与数据结构知识点记录 zhanweifu

  8. 数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Rabin+Pollard_Rho)

    注:转载本文须标明出处. 原文链接https://www.cnblogs.com/zhouzhendong/p/Number-theory.html 数论算法 剩余系相关 学习笔记 (基础回顾,(ex ...

  9. piezo film 压电相关信息记录 (2018-05-04 更新)

    piezo film 压电相关信息记录 起因需要使用 Piezo 做一些设计 http://www.te.com.cn/chn-zh/videos/transportation/piezo-film- ...

随机推荐

  1. zabbix安装要求

    zabbix server 和 zabbix Agent都在一个压缩包中,可以从官网上下载源码包,www.zabbix.com/download.php zabbix是C语言编写,压缩包的文件夹介绍: ...

  2. Centos-配置网络或显示当前网络接口状态-ifconfig

    ifconfig 配置网络或显示当前网络接口状态,必须由root用户执行 相关选项 -a 显示所有网络接口信息,包括活动或非活动 -s 显示活动接口简要信息 -v 如果网卡接口出现错误则返回错误信息 ...

  3. Book of Shaders 04 - 网格噪声:Worley Noise

    0x00 思路 假设要生成 4 个网格,可以先在空间中指定 4 个特征点.对于每个像素点,计算它到最近特征点的距离,将这个距离当作结果值输出. #ifdef GL_ES precision mediu ...

  4. 【django】本地开发media用户上传文件访问路径找不到

    当我们在本地开发的时候,会碰到static可以访问,但是用户上传的文件设置在media下不可访问怎么办?settings配置: 接着在你的urls文件添加: from . import setting ...

  5. Java知识系统回顾整理01基础01第一个程序07Eclipse使用----找不到类如何解决?

    一.现象 有时候会碰到如图所示的问题,分明有Hello这个类,并且也有主方法,可是运行就会出现找不到或者无法加载类Hello,或者Class Not Found 异常. 出现这个状况,有多种原因造成, ...

  6. linux centos7使用docker安装elasticsearch,并且用Django连接使用

    一:elasticsearch安装及配置 1:需求分析 当用户在搜索框输入关键字后,我们要为用户提供相关的搜索结果.这种需求依赖数据库的模糊查询like关键字可以实现,但是like关键字的效率极低,而 ...

  7. [源码阅读] 阿里SOFA服务注册中心MetaServer(1)

    [源码阅读] 阿里SOFA服务注册中心MetaServer(1) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(1) 0x00 摘要 0x01 服务注册中心 1.1 服务注册中心简 ...

  8. 如何让程序像人一样的去批量下载歌曲?Python爬取付费歌曲

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 今天来教大家一个自动化爬虫的工具 selenium selenium Se ...

  9. mycat的privileges标签

    参考https://blog.csdn.net/tornadojava/article/details/54948662 privileges标签 对用户的 schema以及表进行精细化的DML权限控 ...

  10. PHP代码审计03之实例化任意对象漏洞

    前言 根据红日安全写的文章,学习PHP代码审计的第三节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完相关知识点,会用一道CTF题目来加深巩固.之前分别学习讲解了in_arr ...