在Java里ArrayList与数组有什么区别_Java数据结构对比说明

数组长度固定且支持基本类型,ArrayList可自动扩容但仅存对象;数组功能简陋需手动实现操作,ArrayList提供丰富方法并集成集合框架;两者随机访问均为O(1),但适用场景不同。

数组长度固定,ArrayList能自动扩容

Java里数组一旦用 new int[5] 创建,长度就锁死了。想加第6个元素?直接抛 ArrayIndexOutOfBoundsException。你得手动写逻辑:新建更大数组、用 Arrays.copyOf() 搬数据、再赋值——三步缺一不可。

ArrayListadd() 时完全不用操心容量。它默认初始容量是10,满后自动按1.5倍扩容(比如从10→15→22)。但注意:扩容本身是O(n)操作,频繁在循环里 add() 会反复复制数组,拖慢性能。

  • 别在 for 循环里边遍历边 add(),容易触发多次扩容
  • 如果已知最终大小(比如读取1000行文件),初始化时指定容量:new ArrayList(1000)
  • 数组扩容必须自己实现;ArrayList 把这事封装了,但也藏了隐性开销

数组支持基本类型,ArrayList只能存对象

你可以写 int[] nums = {1, 2, 3},内存里就是三个连续的4字节整数。但 ArrayList 是非法的——泛型不接受基本类型。你只能写 ArrayList,这时每个 int 都会被自动装箱成 Integer 对象,存在堆上,还带对象头开销。

这意味着:高频数值计算(如矩阵运算、大量计数)用数组更快更省内存;而业务逻辑中处理“用户列表”“订单集合”这类天然是对象的概念,ArrayList 更自然。

  • 循环中反复读写 ArrayList.get(i) 会触发拆箱,有额外开销
  • int[] 存百万级数字,内存占用约为 ArrayList 的 1/3~1/2
  • 没有 ArrayList,这是Java泛型擦除导致的硬限制,不是语法疏漏

数组只有 length,ArrayList有一整套操作方法

数组除了 arr.length 和方括号索引(arr[i]),啥都没有。你要删一个元素?得自己挪后面所有值;要查某值是否存在?得手写遍历;要合并两个数组?得调 System.arraycopy()Arrays.copyOf()

ArrayList 直接提供 add()remove(int index)contains()indexOf()addAll()removeIf() 等十几种方法。它实现了 List 接口,能无缝接入整个集合框架。

  • array.length 是字段,list.size() 是方法——别写成 list.length,编译报错
  • remove(Object o)remove(int index) 同名重载,传 int 字面量(如 remove(0))会删索引0,不是删值为0的元素
  • 数组转 ArrayList 别直接用 Arrays.asList(arr)——它返回的是固定大小的List,add() 会抛 UnsupportedOperationException

访问速度几乎一样,但底层机制不同

两者通过索引访问都是 O(1):数组靠内存偏移直接取;ArrayList 底层也是数组(Object[] elementData),只是多了一次边界检查和一次引用解引用。实测差异通常在几纳秒内,日常开发可忽略。

真正影响性能的不是访问,而是场景错配:用 ArrayList 存十万 int 做累加,比用 int[] 慢2~3倍;反过来,用数组实现一个购物车功能,增删商品就得自己维护索引、搬数据、处理越界——代码复杂度飙升,且极易出错。

  • ArrayList.get(i) 内部仍是 elementData[i],没额外跳转
  • 线程环境下,两者都不安全:数组没同步逻辑,ArrayList 也不是线程安全的,需用 Collections.synchronizedList()CopyOnWriteArrayList
  • 数组是语言级结构,ArrayList 是类库级抽象——前者轻量可控,后者功能丰富但多了层封装

最常被忽略的一点:数组能声明并初始化多维(int[][] matrix = new int[3][3]),而 ArrayList 只有一维。你要“列表的列表”,得写 ArrayList>,不仅啰嗦,空指针风险也更高。