让面试官喝碗鸡汤,给他讲讲ArrayList和LinkedList的区别

简介: ArrayList和LinkedList作为我们Java中最常使用的集合类,很多人在被问到他们的区别时,憋了半天仅仅冒出一句:一个是数组一个是链表。这样的回答简直让面试官当场吐血。为了和兄弟们一起打好基础,我们通过实际的使用测试,来好好说一下ArrayList和LinkedList的区别这道经典的面试题。

☀️1.ArrayList和LinkedList是什么?


       在我看来,要想搞清楚ArrayList和LinkedList有什么区别,首先一定得要知道这两个东西到底是什么。因为在我看来,通常拿来被比较有区别的东西,它们大体上一定存在很多相似的地方。为了剖析本质,我们直接看看它们的源码声明。


Arraylist: LinkedList:


        可以看出ArrayList和LinkedList都是List接口下的实现类,而List接口可以说是集合类中最常用的接口了,它是一个元素有序、可以重复、可以为null的集合。而且List接口的元素,都可以直接通过下标索引获取。既然如此,那么说明ArrayList和LinkedList都具有上述的功能,那他们使用起来的效率到底有什么区别呢?


☁️2.ArrayList和LinkedList性能比较


🍑1.插入效率比较


         因为上面我们已经提到过这两者都是主要用来存储元素的集合类,那我们可以使用较大的数据量,来测试一下它们插入的效率如何      


//插入到头部
public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        LinkedList<Integer> list2 = new LinkedList<>();
        long time1 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            list1.add(0,i);
        }
        long time2 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            list2.add(0,i);
        }
        long time3 = System.currentTimeMillis();
        //ArrayList的插入时间
        System.out.println(time2-time1);//58746
        //LinkedList的插入时间
        System.out.println(time3-time2);//124
    }
     //插入到尾部
     public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        LinkedList<Integer> list2 = new LinkedList<>();
        long time1 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            list1.add(i);
        }
        long time2 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            list2.add(i);
        }
        long time3 = System.currentTimeMillis();
        //ArrayList的插入时间
        System.out.println(time2-time1);//23
        //LinkedList的插入时间
        System.out.println(time3-time2);//140
    }

       大家发现没有,当插入100万个元素到头部时,LinkedList的速率竟然是ArrayList五千倍之多,当我们插入100万个元素到尾部时,但是又发现ArrayList的速率比LinkedList还快,这是为什么呢?


        其实做这个实验,是为了打消许多人的误区——LinkedList的插入效率一定比ArrayList要高。没错,理论上确实是如此,因为Arraylist的底层是数组实现,而LinkedList的底层为双向链表。在初学数据结构时链表的插入效率高于数组这是我们就学习过的知识,可实际上在这里其实当数据量越来越大时,ArrayList的插入和删除效率是比LinkedList越来越高的,这涉及到数组和链表在元素操作上的问题。但其实在元素量较少时,两者的效率几乎所差无几。        


🍋2.查询效率比较


           我们向ArrayList和LinkedList同时插入10万条数据,然后去索引每个下标,测试一下两者的查询效率如何        


public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        LinkedList<Integer> list2 = new LinkedList<>();
        //先放入一百万个元素
        for (int i = 0; i < 100000; i++) {
            list1.add(i);
            list2.add(i);
        }
        long time1 = System.currentTimeMillis();
        for (int i = 0; i <list1.size() ; i++) {
            list1.get(i);
        }
        long time2 = System.currentTimeMillis();
        for (int i = 0; i <list2.size() ; i++) {
            list2.get(i);
        }
        long time3 = System.currentTimeMillis();
        System.out.println(time2-time1);//1
        System.out.println(time3-time2);//5479
    }

       从测试的结果来看,并没有出乎我们的意外,因为ArrayList的底层为数组实现,对于任何一个下标的索引都是O(1)的时间复杂度。而LinkedList的底层为双向链表,对于查询索引需要从头部或者尾部去遍历找到下标。

 

🍋3.删除效率比较


我们同样向ArrayList和LinkedList放入100万个元素,然后同样测试从头部删除和从尾部删除有什么区别,来测试一下他们的删除效率。


从尾部删除:


public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        LinkedList<Integer> list2 = new LinkedList<>();
        //先放入一百万个元素
        for (int i = 0; i < 1000000; i++) {
            list1.add(i);
            list2.add(i);
        }
        long time1 = System.currentTimeMillis();
        for (int i = 1000000; i >0 ; i--){
            list1.remove(i-1);
        }
        long time2 = System.currentTimeMillis();
        for (int i = 1000000; i >0 ; i--) {
            list2.remove(i-1);
        }
        long time3 = System.currentTimeMillis();
        //ArrayList的删除时间
        System.out.println(time2-time1);//8
        //LinkedList的删除时间
        System.out.println(time3-time2);//18
    }
                从头部删除:
public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        LinkedList<Integer> list2 = new LinkedList<>();
        //先放入一百万个元素
        for (int i = 0; i < 1000000; i++) {
            list1.add(i);
            list2.add(i);
        }
        long time1 = System.currentTimeMillis();
        for (int i = 1000000; i >0 ; i--){
            list1.remove(0);
        }
        long time2 = System.currentTimeMillis();
        for (int i = 1000000; i >0 ; i--) {
            list2.remove(0);
        }
        long time3 = System.currentTimeMillis();
        System.out.println(time2-time1);//55962
        System.out.println(time3-time2);//14
    }


      大家发现了吗,从尾部删除的时候,ArrayList的速度比LinkedList快,而从头部删除后,ArrayList就不知道被LinkedList甩了几条街去了。其实删除同插入其实是一样的,再次实践一次是想让大家加深印象,能走出误区。


🍅4.实验总结


            之所以会做这几个实验,不仅仅是为了让大家更加深刻的去认识ArrayList和LinkedList,也是想让大家走出一些误区,比如什么LinkedList插入删除一定比ArrayList快啊,ArrayList查询一定比LinkedList快啊,从理论上来说确实如此,但通过实验以后,我们应该这样表达:


         1.在数据量不大时,ArrayList和LinkedList的查询效率其实所差无几,只有在数据量较大时,ArrayList会对比出优势


        2.在插入和删除上,LinkedList并不一定ArrayList效率更好,这与数据量以及插入和删除的位置都是有关系的        


🍍3.面试标准回答


      1.ArrayList底层为数组实现,LinkedList底层为双向链表实现。ArrayList只能作为列表使用,LinkedList还能作为队列,因为实现了Deque接口。


      2.LinkedList在数组中的开销更大,因为它不仅需要存储元素,还需要保存前后结点的地址,而ArrayList更加轻量级。


      3. 在插入和删除效率上,理论上LinkedList优于ArrayList,但这还与数据量与处理的位置有关系,但查询的效率上ArrayList更占有优势    


相关文章
|
3天前
|
Android开发 Kotlin
Android经典面试题之Kotlin的==和===有什么区别?
本文介绍了 Kotlin 中 `==` 和 `===` 操作符的区别:`==` 用于比较值是否相等,而 `===` 用于检查对象身份。对于基本类型,两者行为相似;对于对象引用,`==` 比较值相等性,`===` 检查引用是否指向同一实例。此外,还列举了其他常用比较操作符及其应用场景。
158 93
|
2天前
|
Java 关系型数据库 MySQL
面试官:GROUP BY和DISTINCT有什么区别?
面试官:GROUP BY和DISTINCT有什么区别?
14 0
面试官:GROUP BY和DISTINCT有什么区别?
【多线程面试题十】、说一说notify()、notifyAll()的区别
notify()唤醒单个等待对象锁的线程,而notifyAll()唤醒所有等待该对象锁的线程,使它们进入就绪队列竞争锁。
|
1月前
|
算法 Java
【多线程面试题十八】、说一说Java中乐观锁和悲观锁的区别
这篇文章讨论了Java中的乐观锁和悲观锁的区别,其中悲观锁假设最坏情况并在访问数据时上锁,如通过`synchronized`或`Lock`接口实现;而乐观锁则在更新数据时检查是否被其他线程修改,适用于多读场景,并常通过CAS操作实现,如Java并发包`java.util.concurrent`中的类。
|
1月前
|
Java
【多线程面试题十三】、说一说synchronized与Lock的区别
这篇文章讨论了Java中`synchronized`和`Lock`接口在多线程编程中的区别,包括它们在实现、使用、锁的释放、超时设置、锁状态查询以及锁的属性等方面的不同点。
|
1月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
1月前
|
Java
【Java基础面试四】、介绍一下Java的数据类型
这篇文章介绍了Java的数据类型,包括8种基本数据类型(整数、浮点、字符、布尔)和3类引用数据类型(数组、类、接口),并提供了基本数据类型所占内存空间和数据范围的详细信息。
|
1月前
|
Java C++
【Java基础面试十七】、Java为什么是单继承,为什么不能多继承?
这篇文章讨论了Java单继承的设计原因,指出Java不支持多继承主要是为了避免方法名冲突等混淆问题,尽管Java类不能直接继承多个父类,但可以通过接口和继承链实现类似多继承的效果。
【Java基础面试十七】、Java为什么是单继承,为什么不能多继承?
|
1月前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
|
1月前
|
Java
【Java基础面试三十七】、说一说Java的异常机制
这篇文章介绍了Java异常机制的三个主要方面:异常处理(使用try、catch、finally语句)、抛出异常(使用throw和throws关键字)、以及异常跟踪栈(异常传播和程序终止时的栈信息输出)。