首页 > 其他分享 >hashmap

hashmap

时间:2023-09-17 19:12:19浏览次数:43  
标签:hash hashmap 链表 插入 数组 红黑树 如果

(1)HashMap的底层数据结构是什么?

haashMap最早是在jdk1.2中开始出现的,一直到jdk1.7一直没有太大的变化。但是到了jdk1.8突然进行了一个很大的改动。其中一个最显著的改动就是:之前jdk1.7的存储结构是数组+链表,到了jdk1.8变成了数组+链表+红黑树。

在jdk1.7之中把元素放在一个个数组里面, 后面存放的元素越来越多于是出现了链表,对于数组中每一个元素都可以用一个链表来存储数据.

后面元素越来越多链表越来越长直到链表长度大于8的时候转变成红黑树 查找的时候效率不仅没提高还降低不少榆树把链表变成了适合查找的红黑树

1为什么要在链表长度大于8的时候才转变成红黑树呢

1在链表节点不多的时候 数组加红黑树加链表不一定比数组加链表的效率高因此长度比较长的时候才会转变成红黑树

2.因为hashmap需要频繁扩容,会造成红黑树不断地拆分重组,这是非常耗时间的.所有只有在链表足够长才会转变成红黑树来提升效率

(2)

首先hash的put方法也是就是增加方法,第一个参数是键第二个参数是值组合起来叫做键值对

底层是首先put方法传入键值队

根据计算计算hash值

根基hash算法计算在数组中存放的位置查看是否出现了hash冲突

如果没有冲突直接存储到集和中

如果冲突查看当前列表是数组还是红黑树 红黑树的话直接插入

是链表的话判断插入是否大于等于8如果大于等于8转换为红黑树在插入 如果小于8则直接插入链表

源码

put方法是是调用了put'val方法putval方法有5个参数

第一个参数是我们计算的hash值

第二个是我们传入的key

第三个是我们传入的值

第四个是onlyIfAbsent 也就是当键相同,不修改已存在的值

第五个是evict:如果是false那么数组就处于创建者模式中 所以一般为false

那么首先putval

第一部分判断

Node<K,V>[] tab中tab表示的就是数组。Node<K,V> p中p表示的就是当前插入的节点

(2)第一部分:

if ((tab = table) == null || (n = tab.length) == 0)
      n = (tab = resize()).length;

这一部分表示的意思是如果数组是空的,那么就通过resize方法来创建一个新的数组。

然后需要判断插入的位置是否是冲突的,如果不冲突就直接newNode,插入到数组中即可

如果冲突转到第三部分,处理冲突

在首先判断table[i]中的元素是否与插入的key一样,若相同那就直接使用插入的值p替换掉旧的值e。不相同就判断插入的数据结构是红黑树还是链表,在这里表示如果是红黑树,那就直接putTreeVal到红黑树中。

如果数据结构是链表,首先要遍历table数组是否存在,如果不存在直接newNode(hash, key, value, null)。如果存在了直接使用新的value替换掉旧的。

不存在并且在链表末尾插入元素的时候,会判断binCount >= - 1。也就是判断当前链表的长度是否大于阈值8,如果大于那就会把当前链表转变成红黑树

插入成功之后,还要判断一下实际存在的键值对数量size是否大于阈值。如果大于那就开始扩容了。

 

(3)HashMap是如何实现扩容的?

首先如果超过了数组的最大容量,那么就直接将阈值设置为整数最大值,然后如果没有超过,那就扩容为原来的2倍

第一个else if表示如果阈值已经初始化过了,那就直接使用旧的阈值。然后第二个else表示如果没有初始化,那就初始化一个新的数组容量和新的阈值。

第三部分同样也很复杂,就是把旧数据复制到新数组里面。这里面需要注意的有下面几种情况:

A:扩容后,若hash值新增参与运算的位=0,那么元素在扩容后的位置=原始位置

B:扩容后,若hash值新增参与运算的位=1,那么元素在扩容后的位置=原始位置+扩容后的旧位置。

(4)HashMap是如何解决hash冲突的?

HashMap在JDK1.8版本中是通过链式寻址法以及红黑树来解决Hash冲突的问题,其中红黑树是为了优化Hash表的链表过长导致时间复杂度增加的问题,当链表长度大于等于8并且Hash表的容量大于64的时候,再向链表添加元素,就会触发链表向红黑树的一个转化

(7)HashMap为什么是非线程安全的?

想要解决这个问题,答案很简单,因为源码里面方法全部都是非线程安全的呀,你根本找不到synchronized这样的关键字

标签:hash,hashmap,链表,插入,数组,红黑树,如果
From: https://www.cnblogs.com/zhangseekchu/p/17709486.html

相关文章

  • HashMap 的初始化问题
    最近的两次面试被分别被问到了:如果初始化HashMap的容量为100,那么实际容量会是多少?如果初始化HashMap的容量为20,那么实际容量会是多少?会不会发生扩容?自己想当然的会回答:容量会是满足2的幂次*负载因子>=初始化指定容量的值publicstaticvoidmain(String[]arg......
  • Java中HashMap的底层实现原理
     ......
  • (转)HashMap出现 java.util.ConcurrentModificationException
    Iterator<Integer>keys=gradeMap.keySet().iterator();while(keys.hasNext()){Integeri=keys.next();if(!gradesIds.contains(i)){//keys.remove();gradeMap.remove(i);}......
  • 比较分析Vector、ArrayList和hashtable hashmap数据结构
    线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。[color=green][b]Collection├List│├LinkedList│├ArrayL......
  • String、StringBuffer和StringBuilder的区别,ArrayList和linkedList的区别,HashMap和Has
    一、String、StringBuffer和StringBuilder的区别1.1相关介绍String是只读字符串,并不是基本数据类型,而是一个对象。从底层源码来看是一个final修饰的字符数组,所引用的字符串不能改变,一经定义无法再增删改。每次对String操作都会生成新的String对象。所以对于经常改变内容的字符串最......
  • hashmapjdk1.7死循环问题
    hashmap是在jdk1.7是数组+链表,通过hash计算出数组下标位置以后,如果同一个位置有多个元素,放在链表中,在多线程插入,并同时扩容的并发环境会出现死循环问题头插入法在维护链表元素的过程中,有一个head指针,指向第一个元素,没有尾部指针(未插入需要维护一个尾部指针,才能快递定位在哪里插......
  • HashMap
    1.数据结构HashMap主要用来存放键值对,它基于哈希表的Map接口实现,是常用的Java集合之一,是非线程安全的。HashMap可以存储null的key和value,但null作为键只能有一个,null作为值可以有多个JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要......
  • HashMap的底层原理
    HashMap哈希表(HashTable)是一种用于存储键值对的数据结构,他的底层实现在jdk1.8后是数组+链表+红黑树,在jdk1.8前是数组+链表,他通过哈希函数将键映射到储存桶中,从而实现快速的插入,查找和删除操作。哈希表的实现通常包括一个数组和一个哈希函数,其中数组用于储存键值对,哈希函数将建映......
  • hashmap头插法和尾插法区别
    前言HashMap应该算是Java后端工程师面试的必问题,因为其中的知识点太多,很适合用来考察面试者的Java基础。开场面试官:你先自我介绍一下吧!安琪拉:我是安琪拉,草丛三婊之一,最强中单(钟馗不服)!哦,不对,串场了,我是**,目前在--公司做--系统开发。面试官:看你简历上写熟悉Java集合,Hash......
  • hashMap产生的循环依赖问题
    转:hashMap产生的循环依赖问题 这样就是一个很经典hashMap线程不安全导致的循环依赖,因为是个循环链表,就会导致数组一直重复扩容,导致集合的一个无限大,但是JDK1.8的时候,把头插法改成了尾插法,同时引进了红黑树,当连续扩容32次的时候会转换成红黑树,解决这个循环依赖的问题,但是还是......