JAVA集合的扩容机制

news/2025/1/16 0:59:15/

ArrayList

java">List<Integer> list = new ArrayList<>();

不指定初始长度,默认刚开始赋值{}空集合,当 add 时,初始长度为 10。

java">// 初始容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

当超过10之后,每次扩容为原来的 1.5 倍。核心方法是 grow()。

java">private Object[] grow(int minCapacity) {int oldCapacity = elementData.length;if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, /* minimum growth */oldCapacity >> 1           /* preferred growth */);return elementData = Arrays.copyOf(elementData, newCapacity);} else {return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];}
}

每次扩容都是使用 Arrays.copyOf() 拷贝旧数组,然后操作新数组,最终替换掉旧数组。

Arrays.copyOf() 底层最终调用的是 System.arraycopy()。

java">System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));

System.arraycopy() 就是ArrayList增删改慢的原因。

java">List<Integer> list = new ArrayList<>(11);

若设置了初始容量,则只是最开始的值变了,其他都是一样的。

第一次扩容得到的值是 16

LinkedList

典型的双链表。

结构如下:

java">// 头指针
transient Node<E> first;// 前一个指针    
transient Node<E> last;private static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}
}

 

Vector

当不指定初始容量时,默认也是 10,并且如果没有指定 capacityIncrement(也就是每次扩容的大小时),每次默认扩容之前的一倍。当然也跟 ArrayList 一样,会使用Arrays.copyOf()拷贝数组。

java">public Vector() {this(10);
}public Vector(int initialCapacity) {this(initialCapacity, 0);
}private Object[] grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, /* minimum growth */capacityIncrement > 0 ? capacityIncrement : oldCapacity/* preferred growth */);return elementData = Arrays.copyOf(elementData, newCapacity);
}

比如以下的指定,容量依次是 12 16 20 ...

java">List<Integer> a = new Vector<>(12, 4);

HashMap

注意此分析是针对JDK1.8的。

java">	//HashMap的默认初始长度16static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //HashMap的最大长度2的30次幂static final int MAXIMUM_CAPACITY = 1 << 30;//HashMap的默认加载因子0.75static final float DEFAULT_LOAD_FACTOR = 0.75f;//HashMap链表升级成红黑树的临界值static final int TREEIFY_THRESHOLD = 8;//HashMap红黑树退化成链表的临界值static final int UNTREEIFY_THRESHOLD = 6;//HashMap链表升级成红黑树第二个条件:HashMap数组(桶)的长度大于等于64static final int MIN_TREEIFY_CAPACITY = 64;//HashMap底层Node桶的数组transient Node<K,V>[] table;//扩容阈值,当你的hashmap中的元素个数超过这个阈值,便会发生扩容//threshold = capacity * loadFactorint threshold;

HashMap懒加载,第一次 put 时,才会进行初始化节点数组,初始容量是 16(也就是Node[16]) 。

java">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;}
}

当节点哈希冲突时,刚开始的时候是使用链表解决,当链表中的节点数量超过 TREEIFY_THRESHOLD (也就是8)时,进化为红黑树(避免导致因为链表数量太大导致查询不再是O(1))但是需要注意:在数组中的总 size 没有超过 MIN_TREEIFY_CAPACITY(64)时,优先考虑的是扩容而不是树化。当数组中的键值对数量 size(我们通常获取map的长度就是通过这个值) 超过了阈值 threshold(capacity * loadFactor【默认值开始就是 16 * 0.75 = 12】),则进行扩容

扩容的时候,默认会新建一个新数组,新数组的大小是老数组的两倍

通过 (n - 1)& hash 可以定位到数组的下标,当然,此时n (数组的长度)被定义为 2^n。

当然,当红黑树中的节点下降到 UNTREEIFY_THRESHOLD(也就是6)就退化为链表。 

不直接设置为7就变为链表是为了避免如果一直在边界值变来变去的化,导致一直链化和树化来回切换,影响性能,起到一个缓冲的作用。


http://www.ppmy.cn/news/1521667.html

相关文章

Python 全栈系列266 Kafka服务的Docker搭建

说明 在大量数据处理任务下的缓存与分发 这个算是来自顾同学的助攻1&#xff0c;我有点java绝缘体的体质&#xff0c;碰到和java相关的安装部署总会碰到点奇怪的问题&#xff0c;不过现在已经搞定了。测试也接近了kafka官方标称的性能。考虑到网络、消息的大小等因素&#xff0…

IOS17.0安装巨魔:TrollRestore巨魔发布

&#x1f47b; TrollRestore 17.0 巨魔发布 15.0 - 16.7 RC&#xff08;20H18&#xff09;和17.0。 官网&#xff1a;https://trollrestore.com/ 下载&#xff1a;https://pan.metanetdisk.com/IOS/%E5%B7%A8%E9%AD%94%E7%8E%A9%E5%AE%B6/TrollRestore.com 使用&#xff1a;ht…

点在面内(考虑内环外环)------射线算法

射线算法计算点是否在面内 , 考虑内环和外环的情况 // 判断点是否在线段上 bool on_segment(const studio_point& point, const studio_point& l_beg, const studio_point& l_end) {return std::min(l_beg.lgtd, l_end.lgtd) < point.lgtd && point.lg…

计算机网络 第二章: 物理层概述

文章目录 物理层要实现的功能物理层接口特性机械特性电气特性功能特性过程特性 物理层下面的传输媒体传输媒体的分类 传输方式习题答案 物理层要实现的功能 物理层要实现的功能是在各种传输媒体上, 传输0和1吗进而给其上面的数据链路层提供透明传输比特流的服务. (透明传输比特…

硬件工程师笔试面试知识器件篇——二极管

目录 4、二极管 4.1、基础 二极管原理图 二极管实物图 4.1.1、基本特性 4.1.2、常见类型 4.1.3、工作原理 4.1.4、应用领域 4.2、相关问题 4.2.1、二极管的PN结是如何形成的? 4.2.2、发光二极管(LED)的工作原理是什么? 4.2.3、在电子电路中,二极管通常如何应用?…

MySQL Workbench 的入门指南

前言 MySQL Workbench 是一个官方的图形化工具&#xff0c;用于开发、管理和设计 MySQL 数据库服务器。它提供了丰富的功能&#xff0c;可以帮助数据库管理员、开发者以及DBA们高效地工作。下面是一个MySQL Workbench的入门指南&#xff0c;介绍如何安装和使用它。 安装 MyS…

虚拟机苹果系统MacOS中XCode的安装

1、背景介绍 主机系统Win11&#xff0c;虚拟机VMWare17&#xff0c;苹果系统MacOS 13.6.7 2、Xcode的在线 点击应用市场&#xff0c;输入Xcode搜索&#xff1a; 看来Xcode无法安装在macOS V13上&#xff0c;直接在线安装失败。 3、采用下载安装包的方法进行安装 解决办法参考链…

ChatTTS文本转语音本地Windows环境部署与远程生成AI音频实战流程

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 本篇文章主要介绍如何快速地在Windows系统电脑中本地部署ChatTTS开源文本转语音项目&#xff0c;并且我们还可以结合Cpolar内网穿透工具创建公网地址&#xff0c;随时随…