ArrayList

  1. 特点:按添加顺序排列、可重复、非线程安全;
  2. 底层实现:数组
  3. 扩容原理:初始化集合时,默认容量为 0,第一次添加元素时扩容为 10,容量不够时扩容为原来容量的 1.5 倍。

这里扩容指的是无参构造初始化时的场景。对于指定集合长度的构造函数初始化时,初始容量为指定长度,容量不够时再扩容为原来的 1.5 倍。

下面主要介绍无参构造初始化时的场景。

初始化-构造函数

参数定义:

transient Object[] elementData; // 实际存储数据的数组缓冲区

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 初始数组

默认初始化非常简单,调用无参构造器,初始化一个空数组。真正扩容的逻辑在每次添加元素执行。

/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

添加元素-扩容

添加方法:

public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

size:ArrayList 的长度,可用 List#size() 获取

添加方法一共做了两件事:

  1. 判断数组容量是否足够,不够则给数组扩容;
  2. 向数组中添加指定的元素;

添加元素不用多说,向数组中的下一个位置插入即可。下面着重介绍容量判断的逻辑:

1. 判断容量大小

首先判断当前集合容量大小是否足够,如果不够就调用扩容方法 grow(int minCapacity)。

// 确保集合可以添加下一个元素 minCapacity:当前需要的最小容量
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
} // 计算添加元素需要的最小容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果当前集合为空,判断所需最小容量和默认容量大小,返回较大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
} // 确保集合可以满足需要的最小容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

2. 扩容方法

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
  1. 旧容量为当前数组长度;
  2. 新容量为旧容量的 1.5 倍;(此处通过位运算计算,右移1位相当于除以 2,再加原来值即扩大1.5倍)
  3. 如果新容量小于所需最小容量,则将新容量赋值为所需最小容量;(这里主要执行场景为集合初始化时,旧容量为0,新容量扩大1.5倍后也为0,这时新容量将被赋值为默认容量 10)
  4. 如果新容量超过最大数组大小(int 最大值 - 8),这时会报内存溢出 或扩容为 int 的最大取值。
  5. 最后将原来数组数据复制到扩容到新容量大小的数组中。

添加元素-实际流程

首先初始化一个 ArrayList,向里面循环添加 11 个元素。下面针对于第一次添加和第11次添加分别查看 数组初始化 和 数组扩容的逻辑。

public static void main(String[] args) {
List<string> list = new ArrayList<>(); for (int i = 0; i < 11; i++) {
list.add("items");
}
}

数组初始化

ArrayList 初始化后,底层会初始化一个空的对象数组 (elementData),长度 (size) 为 0。当第一次添加元素时,会将其初始化为一个长度为 10 的数组。下面看第一次添加元素的流程:

1.进入添加方法 add(E e)

2.判断容量大小

3.计算所需最小容量

当前数组为空数组,所以if 条件成立,DEFAULT_CAPACITY = 10,minCapacity = 1;

当前方法返回 10;

4.判断数组是否扩容

elementData 当前为空数组,length = 0;minCapacity 为上一步返回的 10;所以此处会调用扩容方法 grow()

5.数组扩容(初始化数组)

这里会根据上面指定的默认容量 10 来给数组扩容。

  • oldCapacity = 0;
  • newCapacity = 0;
  • newCapacity = 10;
  • elementData 扩容为容量为10 的新数组

6.添加元素

  • elementData[0] = items
  • size++ = 1;

数组扩容

根据之前程序的运行,集合保存数据的数组在第一次添加元素时扩充容量为 10,所以在第 11 次添加元素时就会调用扩容的逻辑。

1、add() 方法

minCapacity = size + 1 = 11;

2、判断容量大小

3、计算所需最小容量

​ 非第一次 直接返回

4、判断是否扩容

所需最小容量大于数组长度,调用扩容方法。

5、扩容

  • oldCapatity = 10;
  • newCapatity = 15;
  • 拷贝数组内容到一个 容量为 15 的数组中,完成扩容

6、添加元素

  • elementData[10] = e;
  • size ++ = 11;

ArrayList 深入浅出的更多相关文章

  1. Java容器深入浅出之List、ListIterator和ArrayList

    List是Collection接口的子接口,表示的是一种有序的.可重复元素的集合. List接口的主要实现类ArrayList和Vector,底层都是维护了一套动态的,可扩展长度的Object[]数组 ...

  2. 深入浅出!阿里P7架构师带你分析ArrayList集合源码,建议是先收藏再看!

    ArrayList简介 ArrayList 是 Java 集合框架中比较常用的数据结构了.ArrayList是可以动态增长和缩减的索引序列,内部封装了一个动态再分配的Object[]数组 这里我们可以 ...

  3. 深入浅出Struts2+Spring+Hibernate框架

    一.深入浅出Struts2 什么是Struts2? struts2是一种基于MVC的轻量级的WEB应用框架.有了这个框架我们就可以在这个框架的基础上做起,这样就大大的提高了我们的开发效率和质量,为公司 ...

  4. 计算机程序的思维逻辑 (38) - 剖析ArrayList

    从本节开始,我们探讨Java中的容器类,所谓容器,顾名思义就是容纳其他数据的,计算机课程中有一门课叫数据结构,可以粗略对应于Java中的容器类,我们不会介绍所有数据结构的内容,但会介绍Java中的主要 ...

  5. 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)

    上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...

  6. Scala 深入浅出实战经典 第49课 Scala中Variance代码实战(协变)

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  7. Java编程的逻辑 (38) - 剖析ArrayList

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  8. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  9. WPF学习之深入浅出话模板

    图形用户界面应用程序较之控制台界面应用程序最大的好处就是界面友好.数据显示直观.CUI程序中数据只能以文本的形式线性显示,GUI程序则允许数据以文本.列表.图形等多种形式立体显示. 用户体验在GUI程 ...

随机推荐

  1. TVM部署预定义模型

    TVM部署预定义模型 本文通过深度学习框架量化的模型加载到TVM中.预量化的模型导入是在TVM中提供的量化支持之一. 本文演示如何加载和运行由PyTorch,MXNet和TFLite量化的模型.加载后 ...

  2. 机器学习PAL数据可视化

    机器学习PAL数据可视化 本文以统计全表信息为例,介绍如何进行数据可视化. 前提条件 完成数据预处理,详情请参见数据预处理. 操作步骤 登录PAI控制台. 在左侧导航栏,选择模型开发和训练 >  ...

  3. 相机自动对焦AF原理

    相机自动对焦AF原理 AF性能是判断相机好坏的重要指标,主要从准确度和速度两个方面来进行考察,本文将介绍自动对焦的几种方式. 一.凸透镜成像原理 二.三种对焦方法 有公式在手,只要给相机安个测距仪就好 ...

  4. 构建可扩展的GPU加速应用程序(NVIDIA HPC)

    构建可扩展的GPU加速应用程序(NVIDIA HPC) 研究人员.科学家和开发人员正在通过加速NVIDIA GPU上的高性能计算(HPC)应用来推进科学发展,NVIDIA GPU具有处理当今最具挑战性 ...

  5. 实战SpringBoot Admin

    长话短说哦,直接查看下文吧 目录 声明 先锋 前提 SpringBoot Admin 介绍 服务端的搭建 客户端的搭建 参数的指南 尾声 声明 见名知意,实战SpringBoot Admin,实战!实 ...

  6. tar与NTP时间同步

    tar备份与恢复 归档和压缩 : 1.方便对零散文件管理    2.减少空间的占用 常见的压缩格式及命令工具: gzip ----> .gz bzip2 ---->.bz2 xz ---- ...

  7. Spring Cloud Alibaba(15)---Sleuth+Zipkin

    SpringCloudAlibaba整合Sleuth+Zipkin 有关Sleuth之前有写过两篇文章 Spring Cloud Alibaba(13)---Sleuth概述 Spring Cloud ...

  8. 2021Qt打包发布教程

    因为最近写了一个程序,然后想着能给室友玩耍,就研究了一下如何打包,写这篇博客记录一下 1. 首先获得程序的Release版本 就是点击这个Release,然后构建一遍 2. 进入构建的release文 ...

  9. 喜鹊开发者(The Magpie Developer)

    搬运文,原文地址:https://div.io/topic/1576 我经常感觉,开发人员很像我们所说的喜鹊,以不停的获取很多小玩意来装饰他们的窝而著称.就像喜鹊一样,开发人员通常都被定义为聪明的.好 ...

  10. C#WebService的创建与发布

    VS中新建项目-Web-ASP.NET Web应用程序 然后确定,选择空模版就可以了 其中CRMService.asmx是点击项目新建Web服务(asmx) 这样基本的功能就能用了,然后就是发布 点击 ...