Java ArrayList 原理及面试题总结
简介
ArrayList 是 Java 集合框架中最常用的动态数组实现,提供了快速随机访问的能力。它是 List 接口的非同步实现,允许包含重复元素,并且可以包含 null 值。本文将简要介绍 ArrayList 的工作原理,并总结一些常见的面试题。
原理
ArrayList 在内部使用一个可动态扩展的数组(Object[] 类型)来存储元素。当添加元素到 ArrayList 时,如果当前数组容量不足以添加新元素,会自动进行扩容。扩容过程中,ArrayList 会创建一个新的数组,其容量为原数组容量的 1.5 倍,并复制原数组中的元素到新数组中。
扩容机制
- 当
ArrayList的大小超过其容量时,需要进行扩容。 - 默认初始容量为 10,加载因子(扩容因子)为 0.5,即当
ArrayList的大小达到 15 时进行扩容。 - 扩容时,创建一个新数组,其长度为原数组长度的 1.5 倍,并将原数组元素复制到新数组中。
扩容复制使用
System.arraycopy方法,这是定义在java.lang.System类中的一个静态 native 方法,在其底层实现中,会根据传入的参数计算源数组和目标数组的内存地址,然后执行内存块的复制操作。这个操作通常是通过调用操作系统提供的内存复制函数来完成的,例如在 Linux 系统中可能会使用memcpy函数。
面试题总结
1. ArrayList 能否包含 null 值?
可以。ArrayList 允许存储 null 值,但只允许存储一个 null 元素。
2. ArrayList 与 LinkedList 的区别是什么?
- 访问性能:
ArrayList提供快速的随机访问能力,而LinkedList在随机访问时性能较差。 - 内存消耗:
ArrayList占用的内存较少,LinkedList需要额外存储节点引用。 - 插入和删除:
ArrayList在非尾部插入或删除元素时需要移动后续元素,而LinkedList可以在任意位置高效地进行插入和删除操作。
3. ArrayList 的初始容量和负载因子是什么意思?
ArrayList 有初始容量的概念,这是指创建 ArrayList 时数组的大小。但需要注意的是,ArrayList 没有负载因子的概念,这是 HashMap 的一个特性。负载因子用于决定 HashMap 的扩容时机。
4. ArrayList 的扩容机制是怎样的?
当 ArrayList 的元素数量超过其当前容量时,会自动扩容。扩容时,会创建一个新的数组,其长度为原数组长度的 1.5 倍,并将原数组中的元素复制到新数组中。
5. ArrayList 为什么不是线程安全的?
ArrayList 并不是为线程安全设计的。它的所有操作都不是同步的,这意味着在多线程环境下,如果多个线程同时修改 ArrayList,可能会导致不可预测的结果。
6. 如何实现一个不可修改的 ArrayList?
可以使用 Collections.unmodifiableList 方法来创建一个不可修改的 ArrayList。任何试图修改这个 ArrayList 的操作都会抛出 UnsupportedOperationException 异常。
7. ArrayList 的 get(int index) 方法时间复杂度是多少?
ArrayList 的 get(int index) 方法时间复杂度为 O(1),因为它直接通过索引访问数组元素。
8. ArrayList 的 add(E e) 方法在什么情况下会抛出异常?
ArrayList 的 add(E e) 方法通常不会抛出异常,除非 e 是 null 并且 ArrayList 禁止存储 null 值,或者 e 的类型与 ArrayList 存储的元素类型不兼容。