【Android面试基础】ArrayList的随机访问和顺序访问的区别?

💡 原文中文,约7200字,阅读约需18分钟。
📝

内容提要

ArrayList是一种动态调整大小的数组数据结构,它可以高效地进行随机访问和顺序访问。随机访问通过索引直接获取元素,效率高;顺序访问通过迭代器进行遍历,效率也高。ArrayList实现了List接口以及其他一些标记接口,如RandomAccess、Cloneable和Serializable。ArrayList是非线程安全的,而Vector是线程安全的。ArrayList的扩容策略是每次扩容为当前容量的1.5倍,当容量超过Integer.MAX_VALUE时,抛出异常。

🔎

延伸解读

随机访问与顺序访问的性能差异

文章通过实验对比了ArrayList的随机访问(索引直接获取)和顺序访问(迭代器遍历)的性能。在大数据量下,随机访问效率更高,因为顺序访问需要额外创建Iterator对象并调用next()方法,引入开销。但数据量较小时(如小于1000),差异不明显。这提醒开发者根据数据规模选择合适的遍历方式。

标记接口的作用与意义

ArrayList实现了RandomAccess、Cloneable和Serializable等标记接口。RandomAccess表明支持快速随机访问,ArrayList和Vector基于数组实现,随机访问为O(1),而LinkedList基于链表,随机访问为O(n),因此未实现该接口。Cloneable和Serializable分别标识支持克隆和序列化,但需注意ArrayList的clone()是浅拷贝,序列化要求元素也实现Serializable。

非线程安全与modCount机制

ArrayList非线程安全,其modCount字段记录结构修改次数。迭代器在创建时保存expectedModCount,每次操作检查modCount是否一致,不一致则抛出ConcurrentModificationException。因此,在迭代过程中直接增删元素会引发异常,应使用迭代器自身的remove()方法。若需线程安全,可考虑Vector或使用Collections.synchronizedList。

扩容策略与性能优化

ArrayList默认初始容量为10,但首次添加元素时才初始化。扩容时,新容量为旧容量的1.5倍(oldCapacity + (oldCapacity >> 1)),当超过Integer.MAX_VALUE - 8时,会尝试分配Integer.MAX_VALUE,若仍不足则抛出OutOfMemoryError。为优化性能,可预估数据量并指定初始容量以减少扩容次数;删除元素时,System.arraycopy操作可能成为瓶颈,可考虑其他数据结构。

❓

Q&A

ArrayList的随机访问和顺序访问有什么区别?

ArrayList的随机访问通过索引直接获取元素,时间复杂度为O(1),而顺序访问通过迭代器遍历,虽然效率也高,但会引入额外开支。

ArrayList的扩容策略是什么?

ArrayList的扩容策略是每次扩容为当前容量的1.5倍,当容量超过Integer.MAX_VALUE时,会抛出异常。

ArrayList是否线程安全?

ArrayList是非线程安全的,而Vector是线程安全的。

ArrayList实现了哪些接口?

ArrayList实现了List接口以及RandomAccess、Cloneable和Serializable等标记接口。

在大数据量情况下,ArrayList的随机访问效率如何?

在大数据量情况下,ArrayList的随机访问效率高于顺序访问,因为顺序访问会引入额外的开支。

ArrayList中的元素是如何存储的?

ArrayList中的数组存储的是Object类型,基本类型数据会有装箱和拆箱的开支。

🏷️

标签

➡️

继续阅读