周末的下午,阳光透过咖啡馆的玻璃窗洒在桌上,我和几个技术圈的朋友围坐在一起,聊着最近的项目。突然,阿华提了个问题:“小米,你总是推荐我们用 ArrayList,但它真的就那么完美吗?有什么缺点吗?”

这问题有意思!我端起咖啡,笑了笑:“那今天咱们就聊聊 ArrayList,看看它的优缺点。咱们先从它的优点说起。”

故事从 ArrayList 的结构说起

ArrayList 是 Java 集合框架中最常用的动态数组。简单来说,它就像是一个可以自动扩容的数组。为什么大家喜欢用它?因为它的优点真的太香了!

优点一:动态扩容,灵活方便

先想象一下,你有一个固定大小的数组,装满了数据,突然需要再加点儿东西怎么办?新建一个更大的数组,然后把旧数组的内容复制过去,对吧?费时又费力。

ArrayList 就很贴心了。它会自动扩容,省去了手动管理数组大小的麻烦。比如当元素数量超出容量时,ArrayList 会新建一个更大的数组(默认是原大小的 1.5 倍),然后把旧数据挪过去。这一点在需要频繁动态添加数据的场景中,非常实用!

优点二:随机访问性能优异

ArrayList 是基于数组实现的,这意味着它的每个元素都存储在连续的内存块中。于是,随机访问的时间复杂度是 O(1)。

换句话说,通过索引访问元素非常快!比如你要获取第 i 个元素,直接通过 list.get(i) 就能搞定。

举个例子,假设你写了一个排行榜系统,需要频繁地读取某些排名对应的用户信息,这时用 ArrayList 就非常合适。

优点三:支持所有 List 的常规操作

ArrayList 是 Java 的 List 接口实现类之一,它支持 add()、remove()、contains()、indexOf() 等常用方法,基本能满足各种开发场景。这种丰富的 API 让它成为了很多开发者的首选。

我话音刚落,阿华举手:“等一下,小米,这些优点听起来确实很不错。但你刚才提到它是基于数组实现的,那它是不是也有一些‘天生的局限性’?”

“没错!”我点点头,“正因为它是数组,所以也有一些限制。接下来咱们聊聊它的缺点。”

缺点一:插入和删除性能较差

由于数组的连续性,如果你要在 ArrayList 中间插入一个元素,比如第 i 个位置,后面的所有元素都需要往后挪动一位。这导致插入操作的时间复杂度是 O(n)。

同理,当你删除某个位置的元素时,后面的元素也需要挪动来填补空位,这也是 O(n) 的复杂度。

假如你写了一个聊天软件的消息列表,每当用户撤回消息时,系统需要从 ArrayList 中删除这条消息。这种情况下,如果消息列表特别长,性能就会受到影响。

缺点二:扩容时的性能开销

虽然扩容是 ArrayList 的亮点之一,但这个过程并非没有代价。每次扩容时,都会新建一个更大的数组,并把旧数组的数据复制过去。这一步的复杂度是 O(n)。

如果你频繁地添加大量元素,尤其是当数据规模特别大时,这种扩容的成本可能会拖慢程序的性能。

解决方案?提前设置合适的初始容量,避免频繁扩容。比如用 new ArrayList<>(1000),为 ArrayList 提供一个大概的容量估计。

缺点三:线程不安全

默认情况下,ArrayList 是线程不安全的。如果多个线程同时操作同一个 ArrayList 实例,就可能出现数据不一致的问题。

比如两个线程同时调用 add() 方法,一个线程还没添加完,另一个线程已经开始添加了,这可能会导致某些元素被覆盖或丢失。

解决办法是:

  • 如果需要线程安全,可以用 Collections.synchronizedList() 包装 ArrayList;
  • 或者直接使用 CopyOnWriteArrayList 这种线程安全的实现类。

缺点四:不能存储基本数据类型

“哎,这点我早就吐槽过了!”坐在旁边的小李接过话,“我以前写程序,想把整型数组转成 ArrayList,结果发现得先装箱成 Integer。”

确实如此,ArrayList 的设计是为了存储对象类型,而不是 Java 的基本数据类型。你需要用自动装箱和拆箱,把 int 转成 Integer,再存进列表。这种操作有时会带来额外的性能开销。

好消息是,Java 8 引入了 StreamIntStream 之类的类,部分缓解了这种痛点。

适用场景

说了这么多优缺点,咱们来总结一下,ArrayList 适合用在什么场景?

  • 数据量变化频繁:比如需要动态添加或删除元素时。
  • 随机访问频繁:比如排行榜、缓存等场景。
  • 单线程环境:多线程情况下,需要特别注意线程安全问题。

但如果你的程序需要大量的插入和删除操作,比如双向队列,ArrayList 就不是最佳选择了。此时可以考虑 LinkedList

一段代码总结

为了让大家对 ArrayList 的使用有更直观的感受,我写了一个简单的代码示例:

深入了解 ArrayList:29 岁的我和朋友们的一次茶话会_数组

运行结果:

深入了解 ArrayList:29 岁的我和朋友们的一次茶话会_数组_02

阿华听完点点头:“原来如此,虽然 ArrayList 好用,但也得根据场景来选啊。”

我笑了:“没错,技术从来没有完美的,关键是用对地方。今天这杯咖啡,我请了!”

END

希望这次的分享对你们有帮助。如果你也有喜欢的 Java 集合,欢迎在评论区留言,咱们一起探讨!

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号软件求生,获取更多技术干货!