从源码分析HashMap在JDK8与JDK7的变化

基于JDK8的HashMap源码解析 常见概念解释 常量解释 hash 值的计算 put 操作 resize 扩容操作 阅读详情

简述

在之前的文章中有详细描述过基于JDK7中HashMap与HashTable的差异,而在JDK8出现后,HasMap在实现上有了很大的变化。有所耳闻的就是里面出现了红黑树结构,而且效率更高了。接下来就通过JDK8种HasMap的源码实现来分析其中的变化。

HashMap内部类变化

JDK7中hashMap用来存储真正的Key-Value键值对只有一个Entry的内部类:

//JDK7
static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        .......
}

JDK8中变成了Node和TreeNode内部类:

//JDK8
//Node内部类
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
        .......
}
//JDK8
//TreeNode内部结构(红黑树)
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }
        ......
}

为什么这里会变成了Node和TreeNode,会在HashMap扩容的时候详细说明。
内部结构图:
在这里插入图片描述

put操作

JDK7中HashMap的put操作很简单,put一个value时,通过hash计算,找到对应的数组位置,然后在链表尾部插入这个value。

//JDK7中put操作
public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
        return putForNullKey(value);//针对控制的put操作
    int hash = hash(key);
    int i = indexFor(hash, table.length);//找到插入的位置
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        //如果插入位置已经存在值,则替换为新插入的值
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    addEntry(hash, key, value, i);//插入新value操作
    return null;
}

而在JDK8中put操作就有了许多变化了,比如判断是插入链表还是插入红黑树、从链表裂变成红黑树结构等。

	//JDK8中put操作
	public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    //putVal方法
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //如果table还未初始化,则调用resize()方法初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
            
        //根据key的hash值,找到对应的数组索引,若为null,则该索引位置未被占用过
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {//若该索引对应的数组位置不为null
            Node<K,V> e; K k;
            //当前节点和要插入的节点,key和key的hash值都相同,则是一次替换value的操作
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
             //如果当前节点是红黑树的节点,则以红黑树的形式插入
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {//排除以上两个条件后,则通过遍历链表的方式,进行插入
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //如果插入后链表长度大于等于 8 ,将链表裂变成红黑树
                        //TREEIFY_THRESHOLD = 8;
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //遍历的过程中,如果发现与某个结点的 hash和key,这依然是一次修改操作 
                    if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            //e 不是 null,说明当前的 put 操作是一次修改操作并且e指向的就是需要被修改的结点
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        //插入完成后,当前容量如果大于了阈值,则进行扩容处理
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

resize操作

JDK7中的HashMap,无论put了多少值进入都不会让其数据结构发生变化,所以resize操作就是简单的根据是否达到了阈值,或者是否达到了当前容量最大值,然后进行扩容。

//JDK7
	void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        //loadFactor负载因子0.75f
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

而在JDK8中,当新的value插入后,链表长度大于等于 8 ,将链表裂变成红黑树,所以这里resize就相对复杂一些了,同时hashmap初始化结构的时候,也是用的resize方法。

//JDK8
	final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
        	//当达到极限容量时,则不允许扩容了
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //未达到极限,将数组容量扩大两倍,阈值也扩大两倍
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        //1.老数组中没有任何元素
        //2.构造map时,指定了初始化容量
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               
        	//采用无参构造函数构造的map,并且第一次添加新元素
        	// zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;//使用默认值初始化数组容量
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //newCap = oldThr 之后并没有计算阈值,所以 newThr = 0
        if (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<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {//如果旧数组不为 null,这次的 resize 是一次扩容行为
            for (int j = 0; j < oldCap; ++j) {//将旧数组中的每个节点位置相对静止地拷贝值新数组中
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    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 {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        //不论扩容还是初始化,都可以返回 newTab
        return newTab;
    }

代码中 if ((e.hash & oldCap) == 0) 判断

注意:不是(e.hash & (oldCap-1));而是(e.hash & oldCap)

 (e.hash & oldCap) 得到的是 元素的在数组中的位置是否需要移动,示例如下
示例1:
e.hash=10 0000 1010
oldCap=16 0001 0000
	& = 0 0000 0000       比较高位的第一位 0
结论:元素位置在扩容后数组中的位置没有发生改变

示例2:
e.hash=17 0001 0001
oldCap=16 0001 0000
 &    = 1 0001 0000      比较高位的第一位   1
结论:元素位置在扩容后数组中的位置发生了改变,新的下标位置是原下标位置+原数组长度

(e.hash & (oldCap-1)) 得到的是下标位置,示例如下
e.hash = 10 0000 1010
oldCap-1=15 0000 1111
	&  = 10 0000 1010
	
e.hash  =17 0001 0001
oldCap-1=15 0000 1111
      &  =1 0000 0001

新下标位置
e.hash = 17 0001 0001
newCap-1=31 0001 1111    newCap=32
     &  =17 0001 0001    1+oldCap = 1+16

//元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:
//参考博文:[Java8的HashMap详解](https://blog.youkuaiyun.com/login_sonata/article/details/76598675)  
// 0000 0001->0001 0001

get操作

JDK7中的get操作就是去链表中相应位置找寻key对应的值

//JDK7
	public V get(Object key) {
        if (key == null)
        	//如果是获取key是null的值
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);
        return null == entry ? null : entry.getValue();
    }
    
    //私有方法,对key为null进行处理
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }
    
    //获取结构中的Entry实例
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

JDK8中则多了去寻找红黑树的节点。

//JDK8
	public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    
	final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //先判断数组是否为空,长度是否大于0,那个node节点是否存在
        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;
            if ((e = first.next) != null) {
            	//如果是红黑树,去红黑树找
                if (first instanceof 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;
    }

总结

JDK8中旧链表迁移新链表,链表元素相对位置没有变化; 实际是对对象的内存地址进行操作
在JDK7中旧链表迁移新链表,如果在新表的数组索引位置相同,则链表元素会倒置。
JDK8中的HashMap虽然有了很大变化,但它依然还是线程不安全的,所以在平常使用时,可能会感觉不到它的变化,但知道其内部原理后,在处理多少数据量的时候选用hashMap来存储就有了更清晰的认识。

Java集合框架_第二篇:JDK8之最新改进,HashMap底层特性全解析 文章目录一、前言二、HashMap2.1 HashMap数据结构 + HashMap线程不安全 + 哈希冲突2.1.1 HashMap数据结构2.1.2 HashMap线程不安全2.1.3 哈希冲突2.2 JDK1.7中HashMap的实现2.2.1 基本元素Entry2.2.2 插入逻辑2.2.2.1 put()方法 = hash()方法 + indexFor()方法 + equals()方法2.2.2.2 addEntry() + createEntry()2.2.3 扩容逻辑:resize()2.2. 阅读详情

相关推荐

[JDK8环境下的HashMap类应用及源码分析] capacity实验

通过HashMap的有参构造函数,修改默认capacity,观察此时添加元素时的容量、元素数据、容器数据、下一次修改的阙值等数据

Java 技术专栏,从入门到精通,熟悉企业级开发 2344

java8 HashMap的变化

java8 HashMap的变化树形桶Hash表实现的改变java 7的实现java 8的实现HashMap的一些关键常量总结 在java容器中map是我们经常会用到的一种容器。而map中HashMap是我们最长用的map之一,而且在java面试中考察容器时HashMap的实现也基本是必考之一。并且我们常常会听到考察HashMap时会问到是否是线程安全?为什么HashMap在多线程并发的时候可能造成CPU利用率变成100%?在解析HashMap的这些文章的中,都是基于Java 8之前的的实现来讲解的。那么J

feeling890712的博客 1519

[JDK8下的HashMap类应用及源码分析] 数据结构、哈希碰撞、链表变红黑树

HashMap是一种基于数组和链表(或红黑树)的数据结构,它通过哈希函数将键映射到数组的一个位置,并在该位置存储一个键值对的节点。 HashMap的put方法在插入数据前,首先要计算键的哈希值(hash(key))和索引,然后在相应的位置插入或更新节点,如果节点数超过阈值(threshold),就会进行扩容(resize())或树化。 HashMap的get方法主要是根据键的哈希值和索引,找到对应的位置,然后遍历链表或红黑树,返回匹配的值。

Java 技术专栏,从入门到精通,熟悉企业级开发 2387

jdk7 和jdk8中的hashmap变化:

7中是Entry数组,8中是Node数组7中数组中放的是链表,8中放的是红黑树或链表、7扩容时是正序遍历原来的链表,将节点依次放在链表头,而8是正序遍历原来的链表,将节点放在链表尾;在并发的情况下,7可能出现并发死链的情况。7扩容时在size > threshold的前提下,还要判断null!=table[bucketIndex],即判断要放的那个桶是不是为空,如果都满足,才扩容;而8只要满足size > threshold就扩容。7在插入新节点时,是插在链表头的;8是插在链表尾的,还要判断是不是数量大于8

weixin_49698553的博客 419

Wine7.22实战:如何在Ubuntu上完美运行通达信和微信(附字体配置技巧)

本文详细介绍了如何在Ubuntu系统上使用Wine7.22版本,完美运行通达信和微信这两款Windows专属软件。通过选择稳定的Wine7.22版本、利用现成的兼容容器,并重点解决了中文字体渲染的核心难题,提供了从环境搭建、字体配置到软件安装调优的一站式实战指南,帮助用户在Linux环境下无缝使用关键金融与通讯工具。

weixin_29280657的博客 549

MCNP 减方差技巧(最全)

MCNP的减方差技巧,核心在于不显著增加计算时间的前提下,通过修改模拟过程来降低结果的统计误差。尤其适用于,这类问题中粒子很难到达探测器区域,直接模拟效率极低栅元重要性是常用的减方差技巧,该方法主要对计算模型的不同区域定义不同的重要性数值。当粒子从重要性低的区域废物重要性高的区域,产生更多粒子,增大统计量;当从重要性高的区域进入重要性低的区域,会进行,增快计算效率。n= 中子,N;光子,P;电子 Exi= 栅元i的重要性I=栅元总数项目IMP卡是必须的,除非使用了WWN卡如果粒子进入0。

chencheng1127的博客 926

jdk8中对HashMap做了哪些改变?

在java 1.8中,如果链表的长度超过了8,那么链表将转换为红黑树。(桶的数量必须大于64,小于64的时候只会扩容) 发生hash碰撞时,java 1.7 会在链表的头部插入,而java 1.8会在链表的尾部插入 在java 1.8中,Entry被Node替代(换了一个马甲)。 ...

Leon_Jinhai_Sun的博客 1015

.NET MAUI 第13讲 深入探索 .NET MAUI Essentials:让应用感知设备与环境,打造智能体验

摘要:本文介绍了.NET MAUI Essentials库如何赋能移动应用感知设备环境的能力。文章详细讲解了设备信息获取(DeviceInfo)、网络状态监测(Connectivity)、地理位置服务(Geolocation)、地图集成(Map)以及传感器(Accelerometer等)等核心功能的使用方法,并提供了完整的代码示例。通过一个综合练习,展示了如何构建设备信息仪表板,实时显示设备参数和网络状态,并实现位置获取与地图导航功能。这些跨平台API能显著提升应用智能化和用户体验,同时保持代码简洁高效。

AlphaNil的博客 1087

jdk.8对于:HashMap的改动

在数据个数大于大于64和链表长度大于8就会转换为。,所以总会存在不同的数据经过计算后得到的值。将发生哈希冲突的value下标出改为存储。所谓hash冲突,是由于。这样在遍历时效率将会提升很多。,得到一个数组的下标,然后将。哈希算法被计算的数据是无限的。所以就会存在一个问题,

qx020814的博客 500

Spine动画在Unity里卡顿、闪烁、排序错乱?这些性能优化与渲染问题一次讲清

本文深入解析了Spine动画在Unity中常见的卡顿、闪烁和排序错乱问题,提供了全面的性能优化与渲染解决方案。从性能瓶颈诊断到材质合并、动态合批优化,再到层级错乱与Alpha闪烁的处理,帮助开发者提升动画表现和运行效率。特别针对Unity和Spine的协同工作进行了详细的技术探讨。

weixin_29967445的博客 380

侧杆试验 - 高速撞击 - 显式动力学 - ANSYS Workbench

显式动力学是一种时间积分方法,用于在速度很重要时执行动态模拟。显式动力学考虑快速变化的条件或不连续事件,例如自由落体、高速撞击和施加的负载。由于这些“非线性动力学”已集成到模拟中,因此显式动力学是模拟高度瞬态物理现象的首选。有些侧面碰撞是指车辆侧向撞上路边的坚硬物体,如树木或电线杆。这通常是由于驾驶员失去控制、超速、误判拐角或在湿滑路面上打滑造成的。

jianqimingtian的博客 1289

HashMap 源码分析

HashMap是Java核心的哈希表实现,采用数组+链表+红黑树结构。JDK8优化包括:扰动函数减少哈希冲突;链表长度超8时树化,查询效率从O(n)提升至O(logn);扩容时容量保持2的幂。非线程安全,采用fail-fast机制。相比Hashtable性能更优且支持null键值,LinkedHashMap保持插入顺序,ConcurrentHashMap提供线程安全方案。

weixin_54879811的博客 2258

贝叶斯核机回归估计混合物健康效应 【BKMR包】——实操篇

使用 ggplot2包绘制 h 的截面# 绘制总体风险摘要。

三分钟热度 5382

HashMap源码浅析

文章目录1 HashMap简介2 底层数据结构2.1 JDK1.72.2 JDK1.83 HashMap源码分析(JDK 1.8)3.1 静态属性3.2 成员属性3.3 构造方法3.4 tableSizeFor(int cap)方法3.4 确定数组索引位置3.4.1 hash(Object)3.4.2 桶下标计算公式3.5 put(K,V)3.6 resize()3.7 get(Object)3.8 treeifyBin(Node1 HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(k

Stay Hungry, Stay Foolish! 1400

【YOLOv11改进- 主干网络】YOLOv11+WTConv(ECCV2024): 基于YOLOv11的主干网络改进,大感受野的小波卷积

论文提出了WTConv,这是一个使用级联小波分解的层,并执行一组小卷积核的卷积,每个卷积专注于输入的不同频率带,并具有越来越大的感受野。这个过程能够在输入中对低频信息给予更多重视,同时仅增加少量可训练参数。实际上,对于一个  的感受野,可训练参数数量只随着  的增长而呈对数增长。而WTConv与常规方法的参数平方增长形成对比,能够获得有效的卷积神经网络(CNN),其有效感受野(ERF)大小前所未有。

qq_41304809的博客 1401

JAVA:Spring Boot3 集成 Spring AI 实现 Skill 开发

本文介绍了如何通过Spring Boot和Spring AI实现大模型技能(Skill)开发,让AI具备调用业务系统能力。文章首先解释了Skill的概念,即AI可调用的业务能力,如查询订单、天气等,并展示了执行流程和整体架构。然后详细介绍了实践样例,包括技术版本、Maven依赖配置、AI服务设置、ChatClient创建和Skill技能注册中心的实现。注册中心提供了技能的注册、查询、启用/禁用等功能,支持按类别管理技能,并能够获取所有可用技能列表。通过这种方式,开发者可以轻松为大模型扩展业务功能调用能力。

木头 623

Java集合之轻松上手HashMap(2022珍藏版)

😊基础知识点 + 源码分析 + 面试宝典 你想要的全都有⭐

我是老伯的博客 550

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Java集合看完这个再去看面试题

文章目录集合概览ListList概览ArrayList的源码分析(掌握)jdk1.7时的ArrayListjdk1.8时的ArrayList的变化LinkedList的源码分析(了解)Vector的源码分析(了解)SetSet概览HashSet的源码分析(掌握)HashSet jdk1.7与1.8的不同Map 集合概览 Collection接口:单列集合,用来存储一个一个的对象 - List接口:存储有序的、可重复的数据。->ArrayList、LinkedList、Vector - Set接口:存

weixin_41639009的博客 400

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【集合Map系列二】HashMap

需要重写 hashCode 方法和 equals 方法。当 HashMap 存入 k1 的时候,会执行 hashCode 方法,因为没有重写 hashCode 方法,会去 Object 类找 hashCode 方法,而 object 类的 hashCode 方法返回的时对象的地址。这时候用 k2 去获取,用相同的方式去获取 hashCode 方法,因为内存地址不一样,所以 hashCode 不一样。

优快云站内信: https://i.youkuaiyun.com/#/msg/chat/qyj19920704 708

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java集合

集合框架 Collection接口:单列集合,用来存储一个一个的对象 List接口:存储有序的可重复的数组 —>“动态”数组 常用方法 增:add(Object obj) 删:remove(int index) / remove(Object obj) 改:set(int index, Object ele) 差:get(int index) 插入:add(int index,Object ele) 长度:size() 遍历:Iterator迭代器方式、增强for循环、普通的循环 ArrayLis

boshidunlongxia的博客 191

第11章 java集合

java集合

weixin_49762650的博客 154

第九章_Java集合框架

文章目录Java集合框架Collection接口java.util.Iterator接口Collection子接口:List接口ArrayList的源码分析:**LinkedList的源码分析:****Vector的源码分析:**Collection子接口:SetHashSettreeSetMap接口HashMap底层实现原理HashMap在jdk7中实现原理:HashMap在jdk8中实现原理:HashMap底层属性说明:LinkedHashMap底层实现原理:TreeMap底层实现原理:Properti

Anan的博客 422

深入解析 HashMap:从 JDK 1.7 到 JDK 1.8 的变化及性能优化

HashMap是一个功能强大且高效的键值对存储类,它采用了哈希表结构和链表法解决哈希冲突。随着 JDK 1.8 的发布,HashMap引入了红黑树的支持,提升了大数据量下的性能。在实际开发中,HashMap适用于单线程或低并发的场景,对于高并发的情况,可以考虑使用。了解HashMap的实现原理,能帮助开发者在性能调优和设计时做出更合理的选择。🌟 关注我的优快云博客,收获更多技术干货!🌟。

dm菜鸟编程的专栏 1523

HashMap源码分析与常见八股

当链表的长度大于TREEIFY_THRESHOLD(默认为8)时会开始转化,同时要判断Node数组的个数是否大于MIN_TREEIFY_CAPACITY(默认64),如果小于就会先进行扩容,而不是转化,如果大于就直接转化为红黑树。时,会转化,如果当前数组长度小于64就学会先进行数组扩容,否则就转化为红黑树,默认大小为16,每次扩容2倍。拉链法就是使用一个链表数组,数组中的每一格都是一个链表,如果遇见哈希冲突,则将冲突的值加到链表中。可能是一个空元素,也可能是链表已存在,所以要区别对待。

m0_74047598的博客 2002

jdk8 HashMap红黑树学习

数据结构 jdk7内部数据结构为数组+链表,通过key的hash值计算数据所在数组下标,多个key的hash相同或hash计算的数组下标相同,但是key值不同时,往链表尾追加Entry。 transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; static class Entry<K,V> ...

曾舜尧的专栏 2878
上一篇: HashTable和HashMap的区别
拾光机_Echo
博客等级 码龄14年 3粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值