ArrayList随机访问快是因为底层用连续内存的Object[]数组,下标直接算地址,时间复杂度稳定O(1);但仅限合法索引读操作,需防IndexOutOfBoundsException、多线程不安全、自动装箱NPE及非随机访问场景滥用。

ArrayList 的随机访问为什么快?
因为 ArrayList 底层是连续内存的 Object[] 数组,通过下标计算地址,时间复杂度稳定为 O(1)。只要不触发扩容或越界,get(i) 就是纯指针偏移,比链表、哈希表等结构快得多。
但要注意:这个“快”只针对读,和写操作无关;且前提是索引合法、数组已初始化。
怎么避免 get() 报 IndexOutOfBoundsException
常见错误是循环中用 size() 当上界却没同步检查空状态,或在多线程环境下 size 变化未加锁。
- 始终用
list.size()作为最大合法索引的上界(即合法索引范围是0到list.size() - 1) - 读取前不建议每次都判空,但对不确定来源的
list,优先用if (!list.isEmpty()) { list.get(0); } - 多线程场景下,
ArrayList本身不安全,要么改用Collections.synchronizedList(),要么用CopyOnWriteArrayList(注意后者迭代器不反映实时变更)
get() 性能陷阱:泛型擦除与自动装箱
对基本类型包装类(如 Integer、Boolean)频繁调用 get(),可能因自动拆箱引发隐式 null 检查或 NPE,尤其当元素为 null 时。
例如:int x = list.get(i); 若 list 是 ArrayList 且第 i 位为 null,运行时报 NullPointerException,而非编译报错。
- 若业务允许,优先使用原始类型集合库(如 Eclipse Collections 的
IntArrayList或 Trove 的TIntArrayList) - 否则,在关键路径上显式判空:
Integer val = list.get(i); if (val != null) { int x = val; } - 避免在 tight loop 中反复调用
get(i)做计算——可先提取到局部变量
替代方案:什么时候不该用 ArrayList.get()?
如果访问模式不是“按序/随机下标”,而是“按内容查找”或“需要插入删除中间”,get() 再快也救不了整体性能。
- 要查某个值是否存在?别写
for (int i = 0; i ,改用list.contains(x)(仍是 O(n),但语义清晰)或转成HashSet - 要频繁在头部/中部增删?
ArrayList的remove(int)会移动后续所有元素,O(n),此时应选LinkedList(但注意它的get()是 O(n)!) - 数据量极大(千万级)且只读?考虑内存映射文件或列式结构(如 Apache Parquet),而不是把全量加载进
ArrayList
真正的高效,从来不是单点操作快,而是整个数据访问模式和容器选择匹配。别为了用 get() 而强行把数据塞进 ArrayList。










