Java中的集合框架:ArrayList vs LinkedList的性能对比!

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

  在Java开发中,集合框架(Collection Framework)提供了多种不同的数据结构和算法,帮助我们处理各种不同的存储和操作需求。在这些集合类中,ArrayListLinkedList是两个非常常见的实现类,它们都实现了List接口,但它们的底层结构和性能特点却有所不同。了解它们的差异,对于提高代码的性能和选择合适的集合类至关重要。今天我们就来深入分析ArrayListLinkedList之间的性能差异,帮助你做出更好的选择。


一、ArrayList与LinkedList的基础差异

在开始比较它们的性能之前,我们先来快速回顾一下它们的基本实现和特点。

1. ArrayList

  ArrayList是基于动态数组实现的。它允许我们在数组的基础上动态扩展容量,提供了非常快速的随机访问功能(通过索引访问元素)。每当数组容量不足时,ArrayList会通过创建更大的新数组并将数据复制过去来进行扩容。

  • 底层数据结构:动态数组
  • 访问性能:通过索引直接访问,时间复杂度为O(1)
  • 插入和删除:在数组中间插入或删除元素时,需要移动后面的所有元素,最坏情况下的时间复杂度为O(n)
  • 扩容:当容量不足时,ArrayList会进行扩容(通常是当前数组大小的1.5倍),这个过程是昂贵的,但它发生的频率较低。

2. LinkedList

  LinkedList是基于双向链表实现的。每个节点包含数据和指向前后节点的指针。与ArrayList不同,LinkedList不需要连续的内存空间,因此它的元素分布是离散的。

  • 底层数据结构:双向链表
  • 访问性能:由于元素是分散存储的,所以访问元素时必须遍历链表,时间复杂度是O(n)
  • 插入和删除:在链表的任意位置插入或删除元素时,只需要修改相邻节点的指针,因此其时间复杂度为O(1),但前提是你需要找到要插入或删除的位置(这本身的操作时间复杂度是O(n))。
  • 内存消耗:由于每个节点需要存储指向前后节点的指针,LinkedList的内存消耗比ArrayList要高。

二、性能对比:具体场景下的差异

1. 访问元素的性能:ArrayList占优

  在需要频繁访问集合中元素的场景下,ArrayList表现得非常优秀。由于它底层是基于数组的,支持通过索引直接访问元素,因此时间复杂度为O(1),无论集合的大小如何。

  而LinkedList由于底层是链表结构,访问某个元素时需要从链表的头部或尾部开始遍历,最坏情况下需要遍历整个链表才能访问到某个元素。因此,访问元素的时间复杂度是O(n),性能较差。

示例代码:访问元素
public class ListAccessTest {
    public static void main(String[] args) {
        int size = 1000000;
        List<Integer> arrayList = new ArrayList<>();
        List<Integer> linkedList = new LinkedList<>();
        
        // 填充数据
        for (int i = 0; i < size; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }

        // 测试访问时间
        long start = System.nanoTime();
        arrayList.get(size / 2);
        long end = System.nanoTime();
        System.out.println("ArrayList访问中间元素耗时: " + (end - start) + " 纳秒");

        start = System.nanoTime();
        linkedList.get(size / 2);
        end = System.nanoTime();
        System.out.println("LinkedList访问中间元素耗时: " + (end - start) + " 纳秒");
    }
}

2. 插入和删除元素的性能:LinkedList占优(特别是在中间位置)

  如果你需要频繁地插入或删除元素,尤其是中间位置的插入和删除,LinkedList通常会优于ArrayList。这是因为LinkedList在插入或删除节点时,只需要修改相邻节点的指针,而ArrayList则需要将后续的元素移动一位来保持数组的连续性。

  当然,如果你在ArrayList的末尾进行插入或删除,ArrayList会表现得非常高效,时间复杂度为O(1),与LinkedList相当。但在中间或头部进行插入和删除时,ArrayList的性能明显下降。

示例代码:插入和删除元素
public class ListInsertDeleteTest {
    public static void main(String[] args) {
        int size = 100000;
        List<Integer> arrayList = new ArrayList<>();
        List<Integer> linkedList = new LinkedList<>();
        
        // 填充数据
        for (int i = 0; i < size; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }

        // 测试插入时间
        long start = System.nanoTime();
        arrayList.add(size / 2, 999999);  // 在中间位置插入元素
        long end = System.nanoTime();
        System.out.println("ArrayList插入元素耗时: " + (end - start) + " 纳秒");

        start = System.nanoTime();
        linkedList.add(size / 2, 999999);  // 在中间位置插入元素
        end = System.nanoTime();
        System.out.println("LinkedList插入元素耗时: " + (end - start) + " 纳秒");

        // 测试删除时间
        start = System.nanoTime();
        arrayList.remove(size / 2);  // 删除中间元素
        end = System.nanoTime();
        System.out.println("ArrayList删除元素耗时: " + (end - start) + " 纳秒");

        start = System.nanoTime();
        linkedList.remove(size / 2);  // 删除中间元素
        end = System.nanoTime();
        System.out.println("LinkedList删除元素耗时: " + (end - start) + " 纳秒");
    }
}

3. 内存消耗:ArrayList占优

  由于ArrayList的底层是一个数组,它只存储实际的数据元素,并且数组扩容时采用的是按需扩展,因此内存的使用效率较高。

  而LinkedList每个元素除了存储数据外,还需要存储前驱指针和后继指针,这意味着每个节点占用的内存比ArrayList要大。对于存储大量数据的场景,LinkedList的内存消耗可能会成为一个瓶颈。

4. 线程安全性

  值得一提的是,ArrayListLinkedList都不是线程安全的。如果你在多线程环境中使用它们,并且多个线程同时修改集合,可能会导致数据不一致或者ConcurrentModificationException。在这种情况下,可以使用CopyOnWriteArrayList等线程安全的集合,或者在访问集合时使用同步机制。


三、什么时候选择ArrayList,什么时候选择LinkedList?

那么,面对这两个集合类,我们该如何选择呢?这里给出一些建议,帮助你做出合适的决策。

  1. 选择ArrayList的场景

    • 频繁访问元素:如果你需要频繁地根据索引访问元素,ArrayList是更好的选择。
    • 较少插入/删除操作:如果你的应用程序对插入和删除的需求较少,特别是数组尾部的操作,ArrayList会更高效。
    • 内存敏感ArrayList的内存开销较小,适合内存有限的场景。
  2. 选择LinkedList的场景

    • 频繁插入/删除操作:如果你需要频繁地在中间或头部插入/删除元素,LinkedList能够提供较好的性能。
    • 动态大小的列表:如果集合的大小不确定,并且插入和删除操作很频繁,LinkedList能够更高效地管理元素。

四、结语

  总结来说,ArrayListLinkedList各有优缺点,它们的性能差异源于底层数据结构的不同。作为开发者,我们要根据应用程序的需求,选择合适的集合类。ArrayList在访问元素时具有较高的性能,而LinkedList在插入和删除元素时更加高效。通过深入了解这两者的区别,我们可以在实际项目中做出更加明智的技术选择,从而提高程序的效率和性能。

  希望这篇文章能够帮助你更好地理解ArrayListLinkedList之间的差异,帮助你在实际开发中选择最适合的集合类!

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值