ArrayList扩容机制

简介: ArrayList添加元素前会调用ensureCapacityInternal()进行容量检查,首次添加时默认扩容至10。add()通过grow()实现自动扩容,每次扩容为原容量的1.5倍,确保添加高效。length为数组属性,length()为字符串方法,size()用于集合元素计数。

● 先来看Add方法
/* 将指定的元素追加到此列表的末尾
*/
public boolean add(E e) {
//添加元素之前,先调用ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // Increments modCount!!(增量modCount)
//这里看到ArrayList添加元素的实质就相当于为数组赋值
elementData[size++] = e;
return true;
}
● 再来看看ensureCapacityInternal()方法,可以看到add()方法首先调用了ensureCapacityInternal(size+1)
//得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//获取默认的容量和传入参数的较大值(第一次的较大值是DEFAULT_CAPACITY=10,minCapacity=1)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
当要add进第一个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity为10
● ensureExplicitCapacity()方法
//判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//调用grow()方法进行扩容,调用此方法代表已经开始扩容了
grow(minCapacity);
}
我们来仔细分析一下

  1. 当我们要add进第一个元素到ArrayList时,elementData.length为0(因为还是一个空的list,里面还没有数据,所以没有进行扩容,默认扩容10),因为执行了ensureCapacityInternal()方法,所以minCapacity此时为10。此时,minCapacity - elemetData.length > 0(minCapacity=10,elemetData.length=0)成立,所以会进入==grow(minCapacity)==方法。
  2. 当add第2个元素时,minCapacity为2,此时elementData.length(容量)在添加第一个元素后扩容成10了。此时,minCapacity - elementData.length > 0不成立,所以不会进入(执行)==grow(minCapacity)==方法。
  3. 添加第3、4…到第10个元素时,依然不会执行==grow()==方法,数组容量都为10。
    知道添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进行grow方法进行扩容
  4. grow方法
    private void grow(int minCapacity) {
    // oldCapacity为旧容量,newCapacity为新容量
    int oldCapacity = elementData.length;//(0,10,15)
    //将oldCapacity右移一位,其效果相当于oldCapacity/2;
    // 我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么久把最小需要容量当作数组的新容量
    if (newCapacity - minCapacity < 0)
     newCapacity = minCapacity;
    
    //判断新容量是否大于集合的最大容量(一般大不了)
    if (newCapacity - MAX_ARRAY_SIZE > 0)
     newCapacity = hugeCapacity(minCapacity);
    
    // 给elementData从新赋值(10,15)
    elementData = Arrays.copyOf(elementData, newCapacity);
    }
    int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍!
    “>>”(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源
    通过例子探究一下grow()方法
    ● 当add第一个元素时,oldCapacity为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity不比MAX_ARRAY_SIZE大,则不会进入hugeCapacity方法。数组容量为10,add方法中return true,size增为1。
    ● 当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中rerurn,true,size增为11。
    ● 以此类推…
    这里补充一点比较重要,但是容易被忽视掉的知识点:
    ● java中的length属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了length这个属性。
    ● java中的length() 方法是针对字符串说的,如果想看这个字符串的长度则用到 length() 这个方法。
    ● java中的size() 方法是针对泛型集合说的,如果想看这个泛型有多少元素,就调用此方法类查看!
相关文章
|
1天前
|
存储 缓存 Java
自定义注解
本文介绍自定义注解的实现与应用,结合Spring AOP 和过滤器,实现日志、权限控制等功能。通过@Target、@Retention等元注解定义注解,配合AOP拦截和反射机制,实现如登录验证等业务场景,提升代码可读性与复用性。
|
1天前
|
JSON API 数据库
如何写好一篇技术方案
本模板用于规范产品需求文档,涵盖项目背景、变更记录、功能模块、流程图、API设计等内容,支持语雀卡片与文件附件,提升团队协作效率。
|
1天前
|
Kubernetes Java 应用服务中间件
开发篇(脚手架下载)
本文介绍基于SpringCloud + Kubernetes的微服务开发实践,重点讲解EDAS 3.0如何优化开发者体验。通过阿里云项目初始化工具快速搭建Spring Cloud Alibaba应用,并利用Cloud Toolkit插件实现本地免运维注册中心,一键启动服务,显著提升开发效率。后续将深入部署、联调等环节。
|
1天前
|
SQL 运维 分布式计算
如何做好SQL质量监控
SLS推出用户级SQL质量监控功能,集成于CloudLens for SLS,提供健康分、服务指标、运行明细、SQL Pattern分析及优化建议五大维度,助力用户全面掌握SQL使用情况,实现精细化管理与性能优化,提升日志分析效率与体验。
|
1天前
|
自然语言处理 数据可视化 Docker
安装ES、Kibana、IK
本文介绍如何通过Docker部署单节点Elasticsearch与Kibana,并配置IK分词器。内容涵盖网络创建、镜像加载、容器运行、数据卷挂载、Kibana可视化界面使用及DevTools调试。重点讲解IK分词器的离线安装、扩展词典与停用词典配置,提升中文分词效果。同时提供常见问题解决方案,如ES启动报错处理。全过程适用于学习与测试环境搭建。(238字)
|
1天前
|
存储 监控 Docker
ElasticSearch集群
Elasticsearch集群通过分片与副本机制解决海量数据存储及单点故障问题。将索引拆分为多个shard分布于不同节点,提升存储与性能;通过replica实现数据高可用。利用docker部署多节点集群,结合cerebro监控状态,支持分片管理、故障转移与脑裂防护,确保集群稳定可靠运行。(238字)
|
1天前
|
自然语言处理 关系型数据库 MySQL
数据聚合、自动补全、数据同步
本文介绍了Elasticsearch中数据聚合、自动补全与数据同步的核心功能。通过Bucket、Metric、Pipeline三类聚合,可实现品牌分组、统计计算等实时分析;结合拼音分词器与Completion Suggester,实现搜索框智能提示;并通过MQ或binlog监听实现MySQL与ES的数据同步,提升系统解耦与实时性。
|
1天前
|
运维 安全 Devops
生产环境缺陷管理
git-poison基于go-git实现分布式bug追溯,解决多分支开发中bug漏修、漏发等问题。通过“投毒-解毒-银针”机制,自动化管理bug生命周期,降低协同成本,避免人为失误,已在大型团队落地应用,显著提升发布安全与效率。
|
1天前
|
运维 Java 关系型数据库
微服务概述
微服务架构将单体应用拆分为多个独立、轻量级的服务,各服务围绕业务构建,独立开发、部署与扩展,通过RESTful API通信。它具备服务自治、技术栈灵活、故障隔离等优势,但也带来运维复杂、分布式事务等挑战。自2014年由Martin Fowler等人推动,逐步演进为SpringCloud等成熟方案,成为现代云原生应用的主流架构之一。(238字)
|
1天前
|
存储 负载均衡 Java
Sentinel工作原理
Sentinel 是面向分布式服务架构的流量治理组件,核心概念包括资源与规则。资源指应用中的任意代码块或服务,通过API定义即可受保护;规则则涵盖流量控制、熔断降级和系统保护,支持动态调整。其设计理念强调灵活控流,支持按调用链、QPS、线程数等维度进行限流,采用线程数限制和响应时间降级实现熔断,避免雪崩。同时提供系统负载保护,保障系统稳定。通过Slot链实现资源统计、规则校验与流量调控,支持自定义扩展,具备高可用与低侵入特性。