
本文深入剖析 java 中数组(array)与 arraylist 在内存分配上的本质区别,澄清“arraylist 元素不存储在连续内存中”这一常见误解,明确指出:二者底层均依赖连续内存的 object[] 数组存储引用,差异在于固定容量 vs 动态扩容机制。
本文深入剖析 java 中数组(array)与 arraylist 在内存分配上的本质区别,澄清“arraylist 元素不存储在连续内存中”这一常见误解,明确指出:二者底层均依赖连续内存的 object[] 数组存储引用,差异在于固定容量 vs 动态扩容机制。
在 Java 内存模型中,数组(Array)与 ArrayList 的核心差异并非“是否使用连续内存”,而在于内存布局的层级与管理方式。准确理解这一点,是避免面试误区和写出高效代码的关键。
✅ 正确前提:连续内存指的是“引用的连续”,而非“对象实体的连续”
-
基本类型数组(如
int[]):元素值直接存储在堆上一块连续的内存块中。例如new int[5]分配 20 字节(5 × 4 字节),地址连续、无间隙。int[] primitiveArray = new int[5]; // 内存布局示意(简化): // [0x1000: 0] [0x1004: 1] [0x1008: 2] [0x100C: 3] [0x1010: 4]
-
引用类型数组(如
Integer[]):数组本身(即引用槽)占据连续内存,但每个槽中存放的是指向堆中Integer对象的引用;这些Integer实例本身分散在堆中,不保证连续。Integer[] objectArray = new Integer[5]; for (int i = 0; i < 5; i++) { objectArray[i] = new Integer(i); // 每个 new 都可能分配在不同堆地址 } // 数组本体(5个引用)连续:[ref1][ref2][ref3][ref4][ref5] // 但 ref1 → 0x2A00, ref2 → 0x3F88, ref3 → 0x1E72... 地址彼此无关 -
ArrayList<e></e>:其内部封装了一个Object[](称为elementData),该数组完全等同于Object[]数组——引用存储在连续内存中。ArrayList的“动态性”并非通过非连续存储实现,而是通过扩容时新建更大数组 + 批量复制完成:立即学习“Java免费学习笔记(深入)”;
ArrayList<Integer> list = new ArrayList<>(3); list.add(10); // elementData = [10, null, null] list.add(20); // elementData = [10, 20, null] list.add(30); // elementData = [10, 20, 30] list.add(40); // 触发扩容:new Object[6] → 复制原3个元素 → 更新引用
? 查看
ArrayList源码(JDK 17+)可确认:elementData是transient Object[],所有get()/set()/add()操作均基于该数组的索引计算,地址计算逻辑与普通数组完全一致(base + index * scale),这正是连续内存带来的 O(1) 随机访问基础。
❌ 常见误解辨析
| 说法 | 正误 | 说明 |
|---|---|---|
| “ArrayList 元素不存于连续内存” | ❌ 错误 | 元素引用(elementData[i])严格连续;不连续的是被引用的对象实例 |
| “ArrayList 用链表或散列实现” | ❌ 错误 |
ArrayList 是基于数组的动态封装,非链表(LinkedList 才是) |
| “扩容时旧数组被‘扩展’” | ❌ 错误 | Java 数组不可变大小;扩容 = new Object[newCapacity] + System.arraycopy()
|
⚠️ 关键注意事项
-
性能影响:连续引用带来极快随机访问(
get(i)≈ 数组访问),但插入/删除中间元素需移动后续引用(O(n)),这是ArrayList的固有代价。 -
内存碎片风险:频繁扩容会生成大量短期数组对象,加剧 GC 压力;可通过
ensureCapacity()预分配规避。 -
泛型擦除真相:
ArrayList<string></string>的elementData仍是Object[],类型安全由编译器插入的强制转换保障,运行时无泛型信息。
✅ 总结一句话
数组与 ArrayList 的元素引用均存储在连续内存中;所谓“不连续”实为对对象实例分布的误读。二者的本质区别在于:数组是静态容器,ArrayList 是具备自动扩容能力的数组封装器——它用空间换时间,以可控的复制成本换取 O(1) 访问与灵活容量。


















