MST

星途 面试题库

面试题:Java ArrayList扩容时内存复制机制

在Java ArrayList扩容时,会涉及到将原数组内容复制到新数组。请详细说明这个复制过程是如何实现的,并且分析这个过程对性能的影响以及在高并发场景下可能出现的问题。
44.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

复制过程实现

在Java的ArrayList扩容时,原数组内容复制到新数组是通过System.arraycopy()方法实现。ArrayList的扩容机制如下:

  1. 当向ArrayList中添加元素,且当前数组已满(即size == elementData.length)时,会触发扩容。
  2. 扩容时,新数组大小通常是原数组大小的1.5倍(int newCapacity = oldCapacity + (oldCapacity >> 1);)。
  3. 然后通过System.arraycopy()方法将原数组内容复制到新数组。其方法签名为public static native void arraycopy(Object src, int srcPos,Object dest, int destPos, int length);,在ArrayList中类似System.arraycopy(elementData, 0, newElementData, 0, Math.min(size, newCapacity));,即将elementData从索引0开始的Math.min(size, newCapacity)个元素,复制到newElementData从索引0开始的位置。

对性能的影响

  1. 时间复杂度:复制过程时间复杂度为O(n),因为要遍历原数组的所有元素并复制到新数组,n为原数组元素个数。在数组元素较多时,复制操作会消耗一定时间。
  2. 空间复杂度:扩容时会创建新数组,在复制过程中,原数组和新数组同时占用内存,直到复制完成原数组被垃圾回收,这在短时间内会增加内存开销。

高并发场景下可能出现的问题

  1. 数据不一致:如果多个线程同时对ArrayList进行添加操作导致扩容,可能会出现数据覆盖或丢失。例如线程A和线程B同时触发扩容,A先完成复制,B后完成复制,可能B复制的数据覆盖了A添加的部分数据。
  2. 扩容混乱:不同线程可能会同时判断需要扩容并各自创建新数组进行复制,造成资源浪费且可能导致最终数组状态混乱。
  3. 线程安全问题ArrayList本身不是线程安全的,在高并发下对其扩容操作没有同步机制,除了上述数据问题,还可能引发其他线程安全问题,如迭代过程中数组结构变化导致ConcurrentModificationException异常。