hashMap 底层原理+LinkedHashMap 底层原理+常见面试题

1.源码

img

java1.7 hashMap 底层实现是数组+链表

java1.8 对上面进行优化 数组+链表+红黑树

2.hashmap 是怎么保存数据的。

在hashmap 中有这样一个结构

Node implenets Map.entity{hashkeyvaluenext} 

当我们向hashMap 中放入数据时,其实就是一个

Enity{keyvaue}

在存之前会把这个Entity 转成Node

怎么转的如下:

根据Entity 的key 通过hash 算出一个值 当成Node 的 hash ,key vlaue ,复制到Node 中,对于没有产生hash 冲突前,Node 的next 是null.

img

复制完成后,还需要通过Entity 对象的hash 算出 该 Entiry对象 具体应该放在 hashMap 的那个位置。计算如下 Hash&(lenth-1) 得到的值就是hashMap 对应具体的位置。(lentth是当前hashMap 的长度)。‘

解决hash 冲突

就是不同的元素通过 Hash&(lenth-1) 公式算出的位置相同,现在就启动了链表(单项链表),挂在了当前位置的下面,而链表的元素怎么关联呢,就用到了Node 的next ,next的值就是该链表下一个元素在内存中的地址。

jdk1.7 就是这样处理的,而到了 1.8 以后,就引用了红黑树,1.8以后这个链表只让挂7个元素,超过七个就会转成一个红黑树进行处理(最多是64,超多64 就会重新拆分)。

当红黑树下挂的节点小于等于6的时候,系统会把红黑树转成链表。 Node 在jdk1.8之前是插入l链表头部的,在jdk1.8中是插入链表的尾部的。

hashMap 扩容:

hashMap 会根据 当前的hashMap 的存储量达到 160.75=12 的时候,就是扩容 162=32 依次类推下去。2倍扩容。

扩容后元素是如何做到均匀分的。

img

对上面的总结:

img

LinkedHashMap 源码详细分析(JDK1.8)

我针对LinkedHashMap 的总结有一下几点

1.LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构。该结构由数组和链表+红黑树 在此基础上LinkedHashMap 增加了一条双向链表,保持遍历顺序和插入顺序一致的问题。

2. 在实现上,LinkedHashMap 很多方法直接继承自 HashMap(比如put remove方法就是直接用的父类的),仅为维护双向链表覆写了部分方法(get()方法是重写的)。

3.LinkedHashMap使用的键值对节点是Entity 他继承了hashMap 的Node,并新增了两个引用,分别是 before 和 after。这两个引用的用途不难理解,也就是用于维护双向链表.

4.链表的建立过程是在插入键值对节点时开始的,初始情况下,让 LinkedHashMap 的 head 和 tail 引用同时指向新节点,链表就算建立起来了。随后不断有新节点插入,通过将新节点接在 tail 引用指向节点的后面,即可实现链表的更新

5.LinkedHashMap 允许使用null值和null键, 线程是不安全的,虽然底层使用了双线链表,但是增删相快了。因为他底层的Entity 保留了hashMap node 的next 属性。

6.如何实现迭代有序?

重新定义了数组中保存的元素Entry(继承于HashMap.node),该Entry除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用,从而在哈希表的基础上又构成了双向链接列表。仍然保留next属性,所以既可像HashMap一样快速查找,

用next获取该链表下一个Entry,也可以通过双向链接,通过after完成所有数据的有序迭代.

7.竟然inkHashMap 的put 方法是直接调用父类hashMap的,但在 HashMap 中,put 方法插入的是 HashMap 内部类 Node 类型的节点,该类型的节点并不具备与 LinkedHashMap 内部类 Entry 及其子类型节点组成链表的能力。那么,LinkedHashMap 是怎样建立链表的呢?
  
虽然linkHashMap 调用的是hashMap中的put 方法,但是linkHashMap 重写了,了一部分方法,其中就有 newNode(int hash, K key, V value, Node e)``linkNodeLast(LinkedHashMap.Entry p)

这两个方法就是 第一个方法就是新建一个 linkHasnMap 的Entity 方法,而 linkNodeLast 方法就是为了把Entity 接在链表的尾部。

8.链表节点的删除过程

与插入操作一样,LinkedHashMap 删除操作相关的代码也是直接用父类的实现,但是LinkHashMap 重写了removeNode()方法 afterNodeRemoval()方法,该removeNode方法在hashMap 删除的基础上有调用了afterNodeRemoval 回调方法。完成删除。

删除的过程并不复杂,上面这么多代码其实就做了三件事:

  1. 根据 hash 定位到桶位置
  2. 遍历链表或调用红黑树相关的删除方法
  3. 从 LinkedHashMap 维护的双链表中移除要删除的节点

TreeMap 和SortMap

1.TreeMap实现了SortedMap接口,保证了有序性。默认的排序是根据key值进行升序排序,也可以重写comparator方法来根据value进行排序*具体取决于使用的构造方法*。不允许有null值null键。TreeMap是线程不安全的。

**2.TreeMap基于*红黑树(Red-Black tree)实现*。**TreeMap的基本操作 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。

public class SortedMapTest {public static void main(String[] args) {SortedMap<String, String> sortedMap = new TreeMap<String, String>();sortedMap.put("1", "a");sortedMap.put("5", "b");sortedMap.put("2", "c");sortedMap.put("4", "d");sortedMap.put("3", "e");Set<Entry<String, String>> entry2 = sortedMap.entrySet();for (Entry<String, String> temp : entry2) {System.out.println("修改前 :sortedMap:" + temp.getKey() + " 值" + temp.getValue());}System.out.println("\n");//这里将map.entrySet()转换成listList<Map.Entry<String, String>> list =new ArrayList<Map.Entry<String, String>>(entry2);Collections.sort(list, new Comparator<Map.Entry<String, String>>() {@Overridepublic int compare(Entry<String, String> o1, Entry<String, String> o2) {
// TODO Auto-generated method stubreturn o1.getValue().compareTo(o2.getValue());}});for (Map.Entry<String, String> temp : list) {System.out.println("修改后 :sortedMap:" + temp.getKey() + " 值" + temp.getValue());}}}

附加点上面没有讲到的面试题:

1 HashMap特性?
  HashMap的特性:HashMap存储键值对,实现快速存取数据;允许null键/值;线程不安全;不保证有序(比如插入的顺序)。

2 HashMap中hash函数怎么是是实现的?还有哪些 hash 的实现方式?
  1. 对key的hashCode做hash操作(高16bit不变,低16bit和高16bit做了一个异或);
  2. h & (length-1); //通过位操作得到下标index。

3 扩展问题1:当两个对象的hashcode相同会发生什么?
  因为两个对象的Hashcode相同,所以它们的bucket位置相同,会发生“碰撞”。HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。

4 扩展问题2:抛开 HashMap,hash 冲突有那些解决办法?
  开放定址法、链地址法、再哈希法。

5如果两个键的hashcode相同,你如何获取值对象?
  重点在于理解hashCode()与equals()。
  通过对key的hashCode()进行hashing,并计算下标( n-1 & hash),从而获得buckets的位置。两个键的hashcode相同会产生碰撞,则利用key.equals()方法去链表或树(java1.8)中去查找对应的节点。

6 针对 HashMap 中某个 Entry 链太长,查找的时间复杂度可能达到 O(n),怎么优化?
  将链表转为红黑树,实现 O(logn) 时间复杂度内查找。JDK1.8 已经实现了。

7.如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
  扩容。这个过程也叫作rehashing,因为它重建内部数据结构,并调用hash方法找到新的bucket位置。
  大致分两步:
  1.扩容:容量扩充为原来的两倍(2 * table.length);
  2.移动:对每个节点重新计算哈希值,重新计算每个元素在数组中的位置,将原来的元素移动到新的哈希表中。 (如何计算上面讲的有)

8 为什么String, Interger这样的类适合作为键?
  String, Interger这样的类作为HashMap的键是再适合不过了,而且String最为常用。
  因为String对象是不可变的,而且已经重写了equals()和hashCode()方法了。
  1.不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。
  注:String的不可变性可以看这篇文章《【java基础】浅析String》。
  2.因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。

9.hashmap.put 为什么是线程不安全的。(很重要)

正常情况下 hashmap 在保存数据时,底层是数组+链表+红黑树 但是 你去源码中看时,i发现子啊hashMap 底层没有加任何的多线程中的锁机制,比如: synchronize修饰 ,所以在多线程的情况下 hashMap 的单项链表,

可能会变成一个环形的链表,所以这个链表上的Next元素的指向永远不为null, 所以在遍历的时候就是死循环啊。

9.1HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的

9.2 HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。在hashmap做put操作的时候会调用到以上的方法。现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失

10 ,hashmap 初始化时就生了一个长度为16 的数组。

1.1 为什么初始化时16 而不是别的数字,

1.其实是4或者8 只要是2的N次幂就行,因为hashMap put 元素时,会根据key 进行运算得到一个位置,运算就是,根据key的hash值&hashMap的长度-1(hash&length-1) ,

假如hashMap的长度为16 补充:&运算时,都是1才为1,其他情况都为0

hash值 1010 1010 1000 0000 … 1010

&

lennth-1            0111

你会发现不管hash值为多少,只要 length 的长度是2的N次幂, 那么length-1 的二进制最后一位就是1,所以 hash值&上length-1 最后得到的二进制数字的末位,可能是1 也可能是0,

如果 其长度不是2的n次幂,比如 15 ,那么15-1 =14 的 二进制 0110,那么遇上hash 的到二进制末位,永远就是0了 ,这就侧面的表明了通过计算出来的元素位置的分散性。

为什么不选4,8 这些也是2的N次幂作为扩容初始化值呢?其实8 也行4 也行,但是 我的java 是c语言写的,而c语言是由汇编语言,而汇编的语言来源是机器语言,而汇编的语言使用的就是16进制,对于经验而言,当然就选16喽。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557100.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux时间跳变影响,MONGO 集群 修改linux主机时间后的影响

生产环境是一主一从一仲裁3分片的集群&#xff0c;现在发现其中一个节点比当前时间大了好几天&#xff0c;后使用NTP将时间往回调整副本集上。原来时间是5月3日&#xff0c;当前是4月26日&#xff0c;对此进行了调整。[rootcwdtest1 bin]# dateFri May 3 13:20:31 CST 2019[ro…

Java容器解析——HashMap

前言 HashMap是一个散列表&#xff0c;它存储的内容是键值对(key-value)映射。 1 定义 public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {}由HashMap定义可以看出 1&#xff09; HashMap<K,V>表示…

linux教程第五版第三章课后答案,linux 第三章 章习题.doc

填空题Linux_____可以理解为运行在_____之上&#xff0c;回避直接操作_____和配置文件的应用程序。在Linux中常见的两种图形环境是_____和______。在Linux中的配置文件为____和_____。在Fedor8.0中可以通过____窗口进行主题设置。在“自定义主题”对话框中可以对“控件”、“颜…

xp下添加linux启动项,grub.cfg--XP+ Ubuntu10.04双系统安装后无XP启动项

问题描述&#xff1a;Ubuntu 10.04发布了&#xff0c;随即裸机安装了Windows XP &#xff0c;然后用U盘启动盘安装了Ubuntu 10.04,安装速度飞快(不到10分钟吧)&#xff0c;但是装上后发现在grub启动项里没有XP&#xff0c;郁闷了&#xff0c;花了几分钟终于解决了&#xff0c;呵…

HashMap 源码详细分析(JDK1.8)

1. 概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap。HashMap 最早出现在 JDK 1.2中&#xff0c;底层基于散列算法实现。HashMap 允许 null 键和 null 值&#xff0c;在计算哈键的哈希值时&#xff0c;null 键哈希值为 0。HashMap 并不保证键值对的顺序&…

linux错误码61,Linux编程中的错误码列表

有时可以知道错误的数值&#xff0c;但是如果不知道对应于那个宏定义&#xff0c;也很难知道这个错误描述的是什么。我平时这两种方法来确定&#xff1a;在内核源码中搜索和查看头文件。 在内核源码中搜索一般都可以搜到&#xff0c;而且使用source insight的话也很方便&#x…

linux无限刷屏代码,linux下如何实现简单刷屏

今天为了测试我的LCD有没有坏点写了一个简单的刷屏程序&#xff0c;效果还不错&#xff0c;分析给大家。代码如下&#xff1a;#include #include #include #include #include #include #include #include int main(int argc,char *argv[]){int fbfd 0;struct fb_var_screeninf…

mvc:annotation-driven 注解的作用

1.自动注册映射处理器和适配器 这是该注解的主要功能&#xff0c;添加< mvc:annotation-driven/>注解后&#xff0c;容器中会<自动注册HandlerMapping与HandlerAdapter 两个bean。省去手动注册HandlerMapping和HandlerAdapter的步骤。 当配置了< mvc:annotation-…

linux主机熵值过小,tomcat在linux启动应用慢解决方式

问题&#xff1a;o.a.c.util.SessionIdGeneratorBase - Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [2,741] milliseconds原因&#xff1a;拿Tomcat /dev/random做关键词&#xff0c;一堆详细说明。建议看wiki。https://www.cnblogs.…

@Autowired注解能用在static属性吗?autowired注入static属性上为null

Autowired注解能用在static属性吗&#xff1f; 答案是否定的&#xff0c;我们来测试下&#xff1a; 日志信息已经很明确了&#xff0c;表示static不能被Autowired进行复制。为什么呢&#xff1f;我们现在就来一起了解其原因。 首先将我们的测试环境搭建好&#xff0c; Use…

linux下用c语言写黄金矿工,C语言写的黄金矿工O(∩_∩)O---初稿,能玩了

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼int main(void){initgr();go();getch();closegr();return 0;}void youxiye() /* 游戏页 */{setfillstyle(SOLID_FILL,15);bar(0,0,639,49);setfillstyle(SOLID_FILL,6);bar(0,49,639,349);setcolor(BLUE);line(0,49,638,49);setc…

Swagger介绍及使用

导语&#xff1a; 相信无论是前端还是后端开发&#xff0c;都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力&#xff0c;经常来不及更新。其实无论是前端调用后端&#xff0c;还是后端调用后端&…

linux 安装libcurl4-gnutls-dev,curl / curl.h,libcurl,libcurl4-openssl-dev,libcurl4-nss-dev库之间的区别?...

我有以下curl标题,我仍然可以安装上面提到的libcurl4包 –kafkaMetamorphosis:~$ll /usr/include/curl/total 200drwxr-xr-x 2 root root 4096 Jan 16 13:29 ./drwxr-xr-x 51 root root 20480 Jan 16 13:29 ../-rw-r--r-- 1 root root 7303 Dec 18 01:11 curlbuild.h-rw-r--r--…

c语言输入四个成绩求最高分最低分,数组实现学生成绩录入、求最高分、最低分、平均分和排序...

/*使用数组实现10名学生的成绩录入&#xff0c;并输出这10名学生中成绩的最高分最低分平均分按照分数从大到小排序思考&#xff1a;如何实现从小到大排序&#xff1f;如果学院有5个班&#xff0c;每个班30人&#xff0c;如何处理&#xff1f;求所有成绩中的最高分是哪个班的&am…

c语言课设宿舍管理程序,C语言程序课程设计宿舍管理软件.doc

C语言程序课程设计宿舍管理软件.doc课程设计(论文)题 目 名 称 宿舍管理软件课 程 名 称 C语言程序课程设计学 生 姓 名学 号系 、专 业 信息工程系、信息大类指 导 教 师 黄 同 成目 录1 前言2 需求分析62.1要求62.2任务2.3运行环境2.4开发工具3 概要设计与详细设计73.1设计原…

接口文档生成工具Swagger2的使用

一、什么是Swagger Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法&#xff0c;参数和模型紧密集成到服务器端的代码&#xff0c;允许API来始终保…

Netty详解

一&#xff0e;Netty介绍 1.什么是netty Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架&#xff0c;用以快速开发高性能、高可靠性的网络 IO 程序,是目前最流行的 NIO 框架&#xff0c;Netty 在互联网领域、大数据分布式计算…

有没有检查c语言编译错误的软件,求大佬解答,为什么这个程序编译软件检查不出错误...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include#include /* malloc()等 */#include#include#include /* exit() */#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define OVERFLOW -2typedef int Status; /* Status是函数的类型,其值是函数结果状态代码&…

语法分析器c语言实验报告,词法分析实验报告(C++)..doc

词法分析实验报告(C).词法分析实验报告一、实验目的1. 掌握词法分析的原理。2. 熟悉保留字表等相关的数据结构与单词的分类方法。3. 掌握词法分析器的设计与调试。二、实验内容根据编译中的分词原理,用C语言编写一个C语言的词法分析程序:.三、实验要求1. 输入:任意一个C语言程序…

详解 Java NIO

详解 Java NIO 文件的抽象化表示&#xff0c;字节流以及字符流的文件操作等属于传统 IO 的相关内容&#xff0c;我们已经在前面的文章进行了较为深刻的学习了。 但是传统的 IO 流还是有很多缺陷的&#xff0c;尤其它的阻塞性加上磁盘读写本来就慢&#xff0c;会导致 CPU 使用…