Java ArrayList源码深入分析
作者:niuyongzhi 发布时间:2023-06-16 16:30:26
标签:Java,ArrayList,源码
1.ArrayList 是基数组结构的,需要连续的内存空间
从构造函数可以看出,ArrayList内部用一个Object数组来保存数据。
对于无参构造,ArrayList会创建一个大小为10的初始数组,第一次扩容时会创建默认大小数组。
//无参构造函数,会构造一个空数组。第一次扩容时会创默认大小为10的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] EMPTY_ELEMENTDATA = {};
//如果给定集合大小,会创建一个对应大小的数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
2.对于set和get方法ArrayList效率高,因为可以直接通过下标获取存储对象。
//直接通过角标从数组中取出元素
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
//set 方法,直接通过index 拿到元素,进行修改就行。
public E set(int index, E element) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
3.对于add方法,有两种情况:
第一种是在尾部添加,不需要移动数组。
//Add 方法 ,在末端插入不需要
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
第二种情况在指定位置添加或插入数据,则需要移动数组,效率比较低。如果插入在第0个位置则需要移动整个数组。
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
// * 入的元素在index。则移动的数组应该从index开始,向后移动一个,
//移动的数组长度 size-index 从index开始的数组长度
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
在add时还需要考虑到数组的扩容问题,看ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
//通过无参构造创建为设置初始大小时,判断条件为true
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//创建默认大小的数组,初始大小是10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
添加元素后,元素数量大于数组长度时,进行扩容grow,扩容大小是原数组的1.5倍
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//新增后,数组容量已经大于了数组长度,则进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//新的数组大小是原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//创建一个新长度的数组。
elementData = Arrays.copyOf(elementData, newCapacity);
}
4.对应remove 删除元素操作。如果删除元素在最后一个,不需要移动数组,否则会将被删除元素之后的所有元素都要向前移动,效率比较低。
//删除
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
//取出被删除的元素
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
//移动的数组长度,大于0才需要移动,小于0说明被删元素在最后一个
if (numMoved > 0)
//第一个参数是源数组,
//第二个参数是移动的起始位置:index+1,从被删元素的后一个位置开始
//第三个参数是目标数组
//第四个参数是移动目标位置:index,被删除元素的位置
//第五个参数是移动的数组长度 size - index - 1,从index+1位置开始之后的数组长度
//最终会调用到native方法进行移动。
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
5.System.arraycopy方法参数分析。
第二个参数 index+1是被移动数组的起始位置,即被删元素后一个位置
第四个参数index,是被移动数组的起始位置,即被删元素的位置。
最后一个参数size-index-1,被移动数组的长度,即被删元素后一个位置到元素末尾的长度
public static native void arraycopy(java.lang.Object src, int srcPos, java.lang.Object dest, int destPos, int length);
通过对源码的阅读,也许会对ArrayList有一个更加直接的了解。
来源:https://blog.csdn.net/niuyongzhi/article/details/125814050
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- 简述偶然看到一篇关于阿里新orm框架的文章,好奇的点了进去。开发后端多年,看到这个还是有点兴奋的。常用mysql的orm框架mybatis、
- 简介happens-before是JMM的核心概念。理解happens-before是了解JMM的关键。1、设计意图JMM的设计需要考虑两个
- package cn.hackcoder.beautyreader.broadcast;import android.content.Bro
- 前言在 App 开发过程中,ListView 是 比较很常见的控件,用来处理 列表类的数据展示。当然 Flutter 也是支持的,由于 Fl
- 目录1、以下关于java封装的描述中,正确的是:2、请问所有的异常类皆直接继承于哪一个类?()3、Which statement is tr
- 本文实例实现C#以一个收银付费的小程序演示switch case语法如何使用,读入用户选择,把用户的选择赋值给变量n,再根据用户的输入提示付
- 前言前一阵项目中的上传图片改为上传到阿里上,记录一下实现的过程,方便以后查看。参考资料:官方文档配置Android studio添加依赖de
- import java.awt.AlphaComposite;import java.awt.Color;import java.awt.F
- C sharp (#) 数据类型获取这里研究一下关于c#中如何获取变量类型的问题。首先我们研究一下如何获取单个变量的类型// 问题一:获取单
- 目录一、背景二、推荐方式2.1 自定义的枚举2.2 外部枚举三、总结一、背景平时工作开发过程中,难免会用到状态机(状态的流转)。如奖学金审批
- 本文实例讲述了Android使用ListView实现下拉刷新及上拉显示更多的方法。分享给大家供大家参考,具体如下:今天得需求是做listvi
- 问答小剧场 以下会产生信息丢失的类型转换是( ) A.float a=10;
- 接着上次的实现, 添加 mybatis 查询 orcale 数据库第一步: 新建几个必须的包, 结果如下第二步: 在service包下新建p
- 经常会遇到这样的情况,我们在响应客户端请求的数据的时候需要对数据进行处理,比如数据库中的数据是int型,它可能表示某个枚举,或者其它的逻辑意
- 光流的概念是由一个叫Gibson的哥们在1950年提出来的。它描述是空间运动物体在观察成像平面上的像素运动的瞬时速度,利用图像序列中像素在时
- 1、cmd指令,进入.svn目录,找到wc.db文件 sqlite 3 打开2、 对 svn源代码目录 右键, clean up, 稍等1至
- 如下所示:public class 字符串常用操作 { public static void main(String[] arg
- Android Fragment 动态创建Fragment是activity的界面中的一部分或一种行为。可以把多个Fragment组合到一个
- 这个其实很简单,思路是这样的,就是拿view的宽度,除以点的点的宽度+二个点 之间的间距,就可以算出大概能画出几个点
- 最近遇到一个调试很久的问题,MyBatis 查询 Oracle 数据库查询结果与在客户端查询结果不一致。问题引入测试表、测试数据创建测试表、