HashMap源码解析(jdk1.8)

JDK1.8 Hashmap源码解析 hashmap这样做是为了之后运算(位运算)方便,同时在hash时选择更好的hash函数,以抵消2的n次方带来的不便。index = (n - 1) & hash,如果这一位是0,则index = index,否则,index = index + oldCap。线程2唤醒开始执行扩容,此时线程2指向node1,且next节点为node2,但node2的next节点又为node1,出现死循环。扩容,就是给原来的容量乘2,也就是把原来容量oldCap左移一位,这时2^n的好处就表现出来了。 阅读详情

HashMap简介

java.util.HashMap实现了java.util.Map接口,是存储key、value的一种数据结构。key 和value都可以为null,但是key不可以重复,若是重复则会覆盖。

jdk1.7中,HashMap是数组+链表实现的;
jdk1.8中,HashMap是数组+链表+红黑树实现的。

下面主要介绍jdk1.8的Hash源码。

原理

  • HashMap中首先有个Node数组存储数据,然后根据hash算法算出key的hash值,并通过(n - 1) & hash算出数组的索引,使这个key对应的值存储到数组的索引位置中。
  • 如果出现hash冲突,在原来的基础上延伸链表存储数据。
  • 如果延伸出的链表过长,则转为红黑树降低树的高度,增加查询效率。
  • 如果数组的大小达到扩容的阈值,则扩容数组并迁移数据到扩容后的数组中。

数组加链表的形式
在这里插入图片描述
链表过长,转为红黑树
在这里插入图片描述

源码分析

基本属性

//默认的初始化容量(Node数组的大小)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量(Node数组的大小)
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
//存储数据的Node数组
transient Node<K,V>[] table;
//key、value的集合,用于遍历Map
transient Set<Map.Entry<K,V>> entrySet;
//map大小,也就是存了多少个数据
transient int size;
//map修改次数
transient int modCount;
//阈值(扩容阈值,达到这个值时,Node数组需要扩容)
int threshold;
//负载因子,扩容阈值的的其中一个算法是DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY,因此负载因子决定扩容的频率
final float loadFactor;

构造方法

//无参的构造方法,负载因子默认0.75f,初始化大小是16
public HashMap() {
       this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//带初始化大小的构造方法,负载因子默认值是0.75f,初始化大小是16
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//带初始化大小、负载因子的构造方法
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}
//根据传入的map生成一个新map
public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

查找

java.util.HashMap#getNode
HashMap中数据是存在Node数组中,就是Node<K,V>[] table。如果出现Hash冲突就会在数组的这个位置延伸出链表。Node<K,V>中存的是key、value。
下面的方法是先获取Node,如果Node为空,则返回null,否则返回Node.value。

//根据key获取value
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

java.util.HashMap#getNode
根据key和hash值获取Node,Node里存储的是key、value。

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // always check first node。
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;//这种情况是未产生hash冲突,数组key对应的位置存的Node就是想要的数据
        if ((e = first.next) != null) {//产生hash冲突,key对应的数组位置可能有多个数据,遍历延伸出的链表获取数据
            if (first instanceof TreeNode)//如果是TreeNode类型,调用红黑树方法查找
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {//非红黑树则遍历链表查找数据
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

下面的方法是根据key的hash值,定位到数组的某个位置。
根据key取hash值,是为了数 值更离散。
tab[(n - 1) & hash]是为了让该值落到数组的某个位置,比如数组长度是16,15&hash的值取值范围是0-15,这样根据key就会定位到数组的某个位置。

first = tab[(n - 1) & hash]

插入

map.put(“a”,“aa”);
put方法,把key、value放到map中。

java.util.HashMap#put

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

插入方法

java.util.HashMap#putVal的几个入参:

  • int hash :key的hash值
  • K key :key值,泛型
  • V value :value值,泛型
  • boolean onlyIfAbsent :如果为true,不改变已存在的值。
  • boolean evict:如果为false,存储值的数组正在创建中。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
    	//相当于map是空的,Node数组是空的,因此需要创建或扩容Node数组。
    	//resize方法是扩容
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
    	//该key对应Node数组中的位置为空,需要创建一个Node
        tab[i] = newNode(hash, key, value, null);
    else {//该key对应Node数组中的位置有Node
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))//该Node就对应着传入的key,直接取该Node
            e = p;
        else if (p instanceof TreeNode)//红黑树的处理逻辑 
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {//有hash冲突,需要遍历链表
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        //转为红黑树,TREEIFY_THRESHOLD是链表过长转为红黑树的阈值
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))//找到
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key。找到了这个key对应的Node
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);//空实现,等待扩展
            return oldValue;//返回旧值
        }
    }
    ++modCount;//改变Map次数加1
    if (++size > threshold)//map的大小加1,同时大于扩容的阈值
        resize();//扩容
    afterNodeInsertion(evict);//空实现,等待扩展
    return null;
}

插入方法总结:

  • 先判断Node数组是否为空,为空则扩容数组;
  • 根据key找到Node数组中对应位置的Node。Node为空则创建Node放到该位置;
  • 如果Node不为空,判断该Node是否为该key对应的Node(多个key可能在Node数组的同一个位置,hash冲突导致的),若是则将该Node赋值给临时变量e;
  • 如果Node不为空且该Node是TreeNode类型,走红黑树逻辑放入数据。
  • 如果Node不为空且不是上面两种场景,说明是Node数组中该位置延伸出来的链表,遍历链表判断是否为该key对应的Node,是则放入数据返回Node,否则继续遍历,直到遍历到末尾也没找到Node就创建Node放入数据(期间如果达到树化阈值,则将链表转为红黑树);
  • 如果该key已经存在,则获取key的值并直接返回;
  • 修改次数加1;
  • map大小加1;
  • 如果达到map扩容阈值,执行扩容逻辑;
  • 该key不存在,返回null;

扩容方法

java.util.HashMap#resize

final Node<K,V>[] resize() {
		//扩容前的Node数组
        Node<K,V>[] oldTab = table;
        //扩容前的Node数组大小
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //扩容前的扩容阈值
        int oldThr = threshold;
        //声明扩容后Node数组的大小及扩容阈值
        int newCap, newThr = 0;
        if (oldCap > 0) {//Node数组已经被初始化
            if (oldCap >= MAXIMUM_CAPACITY) {//已达到Node数组的最大值,不需要再扩容
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)//按旧容量和阈值的2倍计算新容量和阈值的大小
                newThr = oldThr << 1; // double threshold,阈值*2
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            //调用 HashMap(int) 和 HashMap(int, float) 构造方法时会产生这种情况,此种情况下 newCap = oldThr,newThr 在第二个条件分支中算出
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
        	//调用 HashMap() 构造方法会产生这种情况。
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {// newThr 为 0 时,按阈值计算公式进行计算
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        //声明扩容后的Node数组
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;//赋值新的Node数组给HashMap
        if (oldTab != null) {//之前HashMap中有数据
            for (int j = 0; j < oldCap; ++j) {//遍历原来的Node数组,准备迁移数据到扩容后的Node数组中
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {//Node数组的第j个元素有数据
                    //将该数组中该位置元素情况方便垃圾回收
                    oldTab[j] = null;
                    if (e.next == null)
                    	//没有产生链表及红黑树,直接迁移数据到新数组中
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                    	//重新映射时,需要对红黑树进行拆分
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                    	//遍历分组迁移链表
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                        	//分组生成两个链表
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {//分组条件
                            	//这是原来的位置那组
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {//这是(原来的位置+oldCap)那组
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
						//下面的两个if将链表头放到扩容后数组的指定位置,这样数据就迁移过来了
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;//返回扩容后的Node数组
    }

扩容方法总结:

  • 计算扩容后的Node数组容量、及扩容阈值。
  • 如果扩容前的Node数组为空,说明没数据,不需要迁移。直接返回扩容后的Node数组。
  • 遍历扩容前的数组,准备迁移数据。
  • 如果数组中该位置为空,说明没有数据,继续下一个循环。
  • 如果数组中该位置有值,且该元素的next元素为空,说明未产生hash冲突,没有延伸出链表及红黑树,直接根据key、hash映射到新数组的指定位置即可。
  • 如果数组中该位置有值,且该元素的next元素是TreeNode,说明该数组位置延伸出红黑树,需要对红黑树进行拆分并重新映射到新数组中。
  • 除了上面两个场景以外,就是链表。遍历链表进行数据迁移。
  • 遍历链表时,通过条件if(e.hash & oldCap) == 0)对链表中的数据进行分组。e.hash&oldCap==0说明该数据在数组原来的索引位置,否则该数据在(数组原来的索引位置+oldCap)的位置。这样就分成两组,分出两个链表。
  • 将两个链表放到新数组中的两个位置。
  • 返回新数组,resize()方法结束。

删除

java.util.HashMap#remove(java.lang.Object)

public V remove(Object key) {
    Node<K,V> e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}

java.util.HashMap#removeNode

final Node<K,V> removeNode(int hash, Object key, Object value,
                            boolean matchValue, boolean movable) {
     Node<K,V>[] tab; Node<K,V> p; int n, index;
     if ((tab = table) != null && (n = tab.length) > 0 &&
         (p = tab[index = (n - 1) & hash]) != null) {
         //定位该key对应Node数组中的位置,如果为空返回null
         Node<K,V> node = null, e; K k; V v;
         if (p.hash == hash &&
             ((k = p.key) == key || (key != null && key.equals(k))))//数组中的Node直接是想要的数据,未产生hash冲突,未生成链表及红黑树
             node = p;
         else if ((e = p.next) != null) {//有hash冲突,生成了链表或红黑树
             if (p instanceof TreeNode)//红黑树的处理逻辑
                 node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
             else {//链表的处理逻辑
                 do {//遍历链表
                     if (e.hash == hash &&
                         ((k = e.key) == key ||
                          (key != null && key.equals(k)))) {//取到了元素
                         node = e;
                         break;
                     }
                     //这样赋值,最后这个p是node的前一个节点
                     p = e;
                 } while ((e = e.next) != null);
             }
         }
         if (node != null && (!matchValue || (v = node.value) == value ||
                              (value != null && value.equals(v)))) {//删除数据
             if (node instanceof TreeNode)//红黑树的删除逻辑
                 ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
             else if (node == p)//没有产生链表及红黑树的场景
                 tab[index] = node.next;
             else
             	//链表处理逻辑。根据上面循环链表的处理,p是node的上一个节点。
                 p.next = node.next;
             ++modCount;//修改次数加1
             --size;//map大小加1
             afterNodeRemoval(node);
             return node;//返回删除的节点
         }
     }
     return null;
 }

上面的代码均已注释且逻辑简单,就不总结了。

深入理解HashMap原理(一)——HashMap源码解析(JDK 1.8) 介绍 HashMap原理是JAVA和Android面试中经常会遇到的问题,这篇文章将通过HashMap在JDK1.7和1.8 中的源码来解析HashMap的原理。 相关源码 数组 采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查... 阅读详情

相关推荐

使用 AFL++ 对 IoT 二进制文件进行模糊测试 - 第一部分

American fuzzy lop 是一款面向安全的模糊测试器,它采用一种新型的编译时插桩和遗传算法,可以自动发现干净、有趣的测试用例,从而触发目标二进制文件中新的内部状态。这显著提高了模糊测试代码的功能覆盖率。AFL 的地址是它已经有一段时间没有更新了。虽然 AFL 仍然运行良好,但有一个新项目 AFL++,它是 AFL 的一个分支,包含许多改进和新功能。AFL++ 的地址是,其源代码位于。在本文中,我们将研究如何使用 AFL++ 对 IoT 二进制文件进行模糊测试。

技术超强的网络安全平台 1085

HashMap超全源码详解(JDK1.8)

HashMap基于哈希表的Map接口实现,是以key-value存储形式存在。JDK1.8版本中其数据结构是:数组+链表+红黑树的形式。下面详解源码及解决的问题。 接口方法:成员变量:1.put方法源码及总结总结:2.get方法源码及总结总结:3.resize(扩容)方法源码及总结总结:由JDK1.7的,数组 + 链表JDK1.8变为:数组 + 链表 + 红黑树具体触发条件为:某个链表连接的个数大于8,并且总的容量大于64的时候,那么会把原来的链表转换成红黑树。这么做的好处是什么:红黑树除了添加元素外,

xiaoyya的博客 987

【KBrowser】基于 Kiwi/chromium 制作全新外壳,将 chromium 代码当作 webview 使用,实现可控核替换

chromium 其实只需几步巧妙的架构,就能拿来“换核”,甚至可以插件化,实现类似webview.apk 的效果。重新设计界面,不再是差不多的回声。而 Kiwi 基于 chrome 77 ,只有 50 MB。优势是兼容好、可安装扩展。

sinat_27171121的博客 2028

HashMap源码详解(1.8)

文章目录HashMap1.8中的HashMap详解属性构造函数hash算法寻址算法: (n-1) & hashput操作扩容(resize)方法转化为红黑树(treeifyBin)static class NodeHashMap 1.8中的HashMap详解 属性 /** * The smallest table capacity for which bins may be treeified. * (Otherwise the table is resized if too many nodes

ClearZero的博客 314

全国大学校园网—拓扑图欣赏

全国大学网络拓扑图(一百张) 典型十张 1、 中央财经大学 2、国防科技大学 3、济南大学 4、西安石油大学 5、南通大学IPV6网络 6、中国矿业大学 7、青海师范大学 8、内蒙古科技大学 9、三峡大学 10、中共江苏省党校 剩余90张 点赞、评论获取 ...

LiBai'S BLOG 5万+

村田电感模型选型指南:.s2p和SPICE netlist在ADS中的表现差异

本文深度解析了村田电感在ADS仿真中.s2p与SPICE netlist两种模型的表现差异。通过对比模型本质、仿真设置及高低频段精度,揭示了.s2p文件基于实测插值而SPICE netlist基于等效电路外推的特性。文章提供了明确的选型策略,帮助工程师根据项目频段、仿真类型和精度需求,在射频与微波电路设计中做出最优模型选择。

k9l0m1的博客 452

HashMap1.8 的源码解析

HashMap的结构为数组+链表+红黑树。

weixin_54227075的博客 505

音乐生成 - 使用LSTM或Transformer模型进行音乐生成,如MIDI音乐生成。

在本篇博客中,我们将介绍如何使用LSTM和Transformer模型生成MIDI音乐。我们将首先概述音乐生成的一般流程,然后深入了解LSTM和Transformer模型的特点和实现。最后,我们将通过Python代码演示如何使用这两种模型生成MIDI音乐。

斌擎科技 1720

JDK1.8之hashMap源码分析

hello,大家好,博主今天分享一篇关于hashMap源码的文章。OK,废话不多说,直接进入正题。HashMap是Java中常用的一种集合类型,它的底层实现是基于哈希表。哈希表是一种以键值对形式存储数据的数据结构,通过哈希函数将键映射到数组中,以达到快速查找的目的。在本文中,我们将深入分析JDK1.8中HashMap的源码实现。

weixin_38682092的博客 567

Python-增加b站视频播放量

如果条件允许的情况下,有代理API更好,每次请求url调用API也更方便。如果没有的话,也不用担心,网上本身也有着很多免费国内代理,不过代理是否可用需要自己花点时间去检测,检测方法笔者这里就不详述了,后面有时间的话会单独针对这一块做详细说明。在开始之前,需准备代理IP。总之,在每次请求url的时候,随机带入某个IP防止被平台检测,便于达到增加播放量预期。大致思路就是针对你的目标代理平台进行IP抓取,并存入到自己本地的IP代理池。本文是该专栏的第1篇,后面会持续更新对应平台干货知识,记得关注。

Python进阶专栏《爬虫实战进阶》,《Pyppeteer从入门到精通》原创作者 5209

069、PCIE属性字段:缓存、顺序与安全

上个月调试一块自研的PCIE采集卡,遇到了一个诡异的问题:主机通过DMA从设备读取图像数据,大部分帧正常,但偶尔会出现局部数据错位,像是缓存没刷干净。用逻辑分析仪抓链路层报文,发现TLP(事务层包)本身完好,但内存中的数据就是不对。排查三天后,终于锁定在设备端TLP的**属性字段(Attribute Field)**配置上——那几位看似不起眼的缓存、顺序控制位,差点让项目延期。今天我们就拆解PCIE事务头中的这个关键区域,看看它如何影响系统性能与数据安全。

z534437748的博客 49

JAVA并发编程:深入解析JDK1.8中HashMap、并发容器ConcurrentHashMap原理及其源码分析

一、HashMap详解 1、基本介绍 1.1 概述

菜鸟技术栈的专栏 1517

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

JDK1.8 HashMap源码解析

// 序列号 private static final long serialVersionUID = 362498820763181265L; // 默认的初始容量大小是16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 //hashmap的最大容量为2的...

zxk082829的博客 193

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

HashMap源码深度解析(JDK 1.8)

HashMap是Java中最常用的集合类之一,它实现了Map接口,提供了键值对的存储方式。在JDK 1.8中,HashMap的实现采用了数组+链表+红黑树的数据结构,相比于JDK 1.7的数组+链表结构,在性能上有了显著提升。

lzz的博客 1216

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

JDK1.8HashMap源码解析

本文HashMap源码解析是基于JDK1.8 1. 数据结构   JDK 1.8中,HashMap底层是由“数组+链表+红黑树”组成,如下图,而在 JDK 1.8 之前是由“数组+链表”组成。   JDK1.8中HashMap引入红黑树的主要原因是为了解决链表太长查找的性能变差的问题。 2. 继承关系    3. HashMap   下面我们一起来分析HashMap类的源码。 3.1 相关字段 /** * 默

道阻且长,行则将至! 365

HashMap源码解析 JDK 1.8

文章目录概要0. 前言0.1 影响HashMap性能的参数0.2 HashMap UML图0.3 HashMap数据存储结构(JDK 1.8)1. 源码解析说明1.1 基本属性1.2 hash() & (tab.length-1) : 确定Table索引值1.3 get() : 获取指定key对应的结点1.3.1 获取红黑树结点1.3.1.1 getTreeNode() : 获取红黑树结...

pre_tender的博客 973

jdk1.8HashMap与ConcurrentHashMap源码解析

文章目录一、基础知识1.1二叉搜索树和红黑树 一、基础知识 说实话,写文章是会上瘾的,就好像小时候去游戏厅投了一个币之后,忍不住想买十个一样。自从上次笔者写了Jdk1.7HashMap与ConcurrentHashMap源码解析之后,就一直手痒想写jdk1.8的,于是就有了这篇文章。 1.1二叉搜索树和红黑树 你以为jdk1.8的HashMap仅由数组和链表组成?Too young too sim...

baidu_41650495的博客 817

史上最详细的 JDK 1.8 HashMap 源码解析

可能是史上最详细的 HashMap 源码解析。

程序员囧辉 12万+

JDK1.8源码解析-HashMap

jdk1.8对hashmap进行了优化,1.8之前hashmap的构成是由数组和链表组成的,而jdk1.8则对bucket容器中的元素结构进行了优化,若使用链表则在大量元素进行查找时会降低效率。 我们不妨从它的源码入手,了解jdk1.8对hashmap所作的优化。 ---------------------------------------------------------------更新...

你好,我是橙序员 370

JDK1.8 HashMap源码一步一图(万字)解析

数据结构 原理说明 jdk1.8中的hashmap采用了数组+链表+红黑树进行数据的存储. 原理图 引入红黑数的好处 引入原因:提升hashmap的性能: 解决1.7发生hash碰撞后链表过长而导致索引效率慢的问题. 利用红黑树快速增删改查的特点将 时间复杂度由原来的O(n)变为O(logn) 应用场景:在链表长度>8的时候,将链表转换为红黑树. 在扩容时桶中元素小于等于6 的时候由红黑树转换为链表。 选择6和8,中间有个差值7可以有效防止链表和树频繁转换 ...

qq_25933841的博客 1421

java中hashmap源码解析(jdk1.8)

java中hashmap常用方法流程解析,面试用

su_xu_chao的博客 742

HashMap jdk1.8 源码解析

本文涵盖内容 HashMap 方法源码解析(构造方法、增删改查) HashMap特点、适用场景 常见面试题 特点,适用场景 线程不安全的 存储结构是 数组 + 链表 + 红黑树(jdk1.8) 允许key、value为null 无序 源码解析 1. 构造方法 public HashMap(int initialCapacity, float loadFactor) { if (in...

CoderThc 146

JDK1.8中HashMap集合源码解析

文章目录简介内部结构源码分析1. 构造方法2. put方法3. get方法4. resize方法 简介 HashMap 主要用来存放键值对,它基于哈希表的 Map 接口实现,是常用的 Java 集合之一。 JDK1.8 之前 HashMap 由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。 JDK1.8 之后 HashMap 的组成多了红黑树,在满足下面两个条件之后,会执行链表转红黑树操作,以此来加快搜索速度。 HashMap是线程不安全

浪人与酒 2353
上一篇: Spring MVC源码详解
下一篇: JKD动态代理及CGLIB动态代理
ludkjava
博客等级 码龄11年 173粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值