ArrayList源码解析
初始化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
操作ArrayList
-
add(Object o)
// 用于存储数据的数组,add方法添加的对象就放在这里面 transient Object[] elementData; // 记录数组的长度 private int size; public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!!确保内部容量,传递给ensureCapacityInternal的是新list的长度 // 上一步没报异常,证明ArrayList的容量支持这次add操作 elementData[size++] = e;// 把对象放进数组 return true; }
-
ensureCapacityInternal 确保内部容量
/** * @param minCapacity 新list的长度 */ private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
-
calculateCapacity
就上述的ArrayList初始化代码可知,第一次调用用add,
elementData
和DEFAULTCAPACITY_EMPTY_ELEMENTDATA
一定指向堆中同一地址。
这个方法会返回一个整数——初始化ArrayList的容量大小,如果minCapacity
小于DEFAULT_CAPACITY
,返回10,反之返回minCapacity
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; private static final int DEFAULT_CAPACITY = 10;// 初始容量 private static int calculateCapacity(Object[] elementData, int minCapacity) { /** * 判断elementData与 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是否指向同一 * 堆内存空间(==用来比较内存地址) */ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
-
ensureExplicitCapacity
protected transient int modCount = 0; private void ensureExplicitCapacity(int minCapacity) { modCount++;// 计数,每调用一次ensureExplicitCapacity自增1 // overflow-conscious code 新长度 减去 当前数组长度 大于 0 则调用grow方法 if (minCapacity - elementData.length > 0) grow(minCapacity); }
-
grow
private static final int MAX_ARRAY_SIZE = Integer. MAX_VALUE - 8;// 2147483639 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length;//获取当前数组长度 int newCapacity = oldCapacity + (oldCapacity >> 1);// 新长度 = 旧长度*1.5 // 新长度和实际扩容长度比较,两者相减小于0证明新长度不够满足实际所需扩容后总长度的需要 if (newCapacity - minCapacity < 0) newCapacity = minCapacity;// 把实际所需扩容后总长度赋值给新长度 // 新长度和最大容量比较,两者相减大于0证明这已经是一个大数组了,调用hugeCapacity if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: // 通常是不会走进上述的两个if中的,这时用数组的拷贝的原理,对ArrayList进行扩容并增加数据 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow 实际所需扩容后总长度小于0,抛异常 throw new OutOfMemoryError(); /** * 判断实际所需扩容后总长度大于最大容量限制吗?这一步是为了限制扩容超出范围,ArrayList最大容量 * Integer.MAX_VALUE=2147483647,就是说最大允许存入2147483647个对象。MAX_ARRAY_SIZE是一个尝试分配* 数组大小的上限,并不是ArrayList实际能够存储的元素数量的上限。在需要扩容超过MAX_ARRAY_SIZE的情况 * 下,ArrayList会尝试使用Integer.MAX_VALUE作为新的容量,但请注意,这并不意味着ArrayList可以真正存* 储这么多元素,因为这将需要大量的连续内存,并且可能受到JVM堆大小的限制 */ return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
-
copyOf
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") // 判断数据存储的数据类型,并生成一个新数组返回 T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); // 调用本地方法,将旧数组的数据拷贝到新数组,并增加新元素 System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
-
-
-
remove
public E remove(int index) { // 判断下标是否越界 rangeCheck(index); modCount++; E oldValue = elementData(index);//获取待移除的元素 int numMoved = size - index - 1;// 计算偏移量 // 偏移量大于0,证明移除的是数组中间的元素 if (numMoved > 0) // 调用本地方法,将旧数组的数据拷贝到新数组 System.arraycopy(elementData, index+1, elementData, index,numMoved); // 因为删除了一个元素,并且上一步将元素向前排,那么最后一个元素肯定就不需要了,置为null,这块内存会由GC来回收 elementData[--size] = null; // clear to let GC do its work return oldValue;//返回移除元素后的新数组 }
rangeCheck
private void rangeCheck(int index) { // 如果 待移除的元素的下标 大于 当前ArrayList的size,抛出异常 if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
由源码可得出结论:ArrayList是有容量限制的,最大值为MAX_VALUE=2147483647
,添加元素时ArrayList的处理方式是生成新数组,把旧数组的数据拷贝进去,最后JVM通过垃圾回收机制回收旧数组所占内存,删除元素时ArrayList的处理方式是在原数组基础上做变更。ArrayList的源码中没有加锁,说明ArrayList是线程不安全类。
本文来自博客园,作者:勤匠,转载请注明原文链接:https://www.cnblogs.com/JarryShu/articles/18176985
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现