从源码分析常见集合的区别之List接口

时间:2022-07-26
本文章向大家介绍从源码分析常见集合的区别之List接口,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

说到Java集合,大家肯定脱口而出List、Set、Map,(想不出来的请自行面壁),今天就详细聊聊大家耳熟能详的List吧。

List接口实现自Collection接口,是Java的集合框架中的一员,List接口下又有ArrayListLinkedList和线程安全的Vector,今天就简单分析一下ArrayListLinkedList的异同以及各自的优势。

ArrayList

引用ArrayList集合中的一段注释:

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

可见,ArrayList是底层实现是一个可变长度的Object类型的数组,当我们给list插入数据时,就会调用ArrayList的add方法,根据Java提供的看不懂的add方法注解来看,当ArrayList插入数据时,是在ArrayList的尾部进行插入,调用ensureCapacityInternal方法使链表长度+1

ensureCapacityInternal方法会检测自身容量,当链表长度大于ArrayList的默认长度时,ArrayList就会调用grow方法进行扩容

你们就不好奇ArrayList的默认长度是多少吗?emmm,这好像是一个冷知识,好吧,我们就new一个对象插入几个元素试试,下断点瞅瞅吧,下边是我写的测试代码,再下边是见证奇迹的时刻! ArrayList<Integer> list = new ArrayList<Integer>(); for (int i = 1; i <= 2000000; i++) { list.add(i); } list.forEach(System.out::println); //关于lambda表达式,见仁见智吧,我觉得是个好东西 看到刚刚的Math.max没,没看到的翻上去看,显而易见,这个三目运算符,似乎暴露了什么,嗯,ArrayList的默认长度是10。划重点,这个地方可能会考。

继续往下走无聊的断点,那根据Java的意思,就是当list的长度超过10的时候就会调用grow方法进行扩容 如果面试官问你:怎么看待ArrayList和LinkedList各自的特性,怎么看?看源码呗。

以我80多年考专八的丰富经验,grow方法就是要确保ArrayList的容量要足够你的用的,以免送你一个数组越界的异常,回归正题,当面试官问你ArrayList、LinkedList的特性时,你会不假思索的“背”出来:ArrayList擅长查询,其查询的时间是一定的,增删慢,LinkedList与ArrayList相反,如果你知道这些,恭喜你,你大学可以毕业了,因为我毕业的时候就知道这个(我谨代表广大毕业生的最低水平),哪为什么ArrayList在插入元素的时候慢呢,看源码: // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); 你品,你细品,Arrays.copyOf....,嗯,是不是这样就真相了,ArrayList在扩容的时候会把原来的数组复制到另一个内存空间更大的数组中,然后把新元素添加到扩容以后的数组中。所以喽,你不慢谁慢。 哪ArrayList为啥查询快呢?

人家通过下标查找,为啥不快,没有理由啊,你说对吧。不过,在查找的时候小心IndexOutOfBoundsException,他这个异常可不是闹着玩的。

LinkedList

小朋友,你是否有很多问号?为什么,LinkedList插入快?

LinkedList是一个由相互引用的节点组成的双向链表,那么当把数据插入至该链表某个位置时,该数据就会被组装成一个新的节点,随后只需改变链表中对应的两个节点之间的引用关系,使它们指向新节点,即可完成插入。

测试:贴代码

talk is cheap,show me your code.

ArrayList

public class List {
    public void array(){
        Scanner sc=new Scanner(System.in);
        int test = sc.nextInt();
        long start = System.currentTimeMillis(); //获取当前时间
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i = 1; i <= test; i++) {
            list.add(i);
        }
        list.forEach(System.out::println);
        long end = System.currentTimeMillis();       // 记录结束时间
        System.out.println(end-start);
    }

    public static void main(String[] args) {
        List list = new List();
        list.array();
    }

}

LinkedList

public class Link {
    public void link(){
        Scanner sc=new Scanner(System.in);
        int test = sc.nextInt();
        long start = System.currentTimeMillis(); //获取当前时间
        LinkedList<Integer> link = new LinkedList();
        for (int i = 1; i <= test; i++) {
            link.add(i);
        }
        link.forEach(System.out::println);
        long end = System.currentTimeMillis();       // 记录结束时间
        System.out.println(end-start);
    }

    public static void main(String[] args) {
        Link link = new Link();
        link.link();
    }

}

总结

ArrayListLinkedList的快慢只是一个相对的,通过看LinkedList源码不难发现,LinkedList在插入数据的时候,会new一个Node节点,新建对象也是一种消耗资源的操作,所以,在数据量较小的时候,如插入500万以上的数据时,ArrayList的速度更快一些,一般情况下我们一般不会同时插入百万级的数据,所以一般都说

ArrayList插入遍历快,增删慢,LinkedList增删快,插入慢。两者皆没有线程安全做处理,而Vector的方法由synchronized关键字修饰所以Vector是线程安全的List。

完结撒花。

代码我会同步在Github,欢迎访问:

我的Github

我们站在巨人的肩膀上,文章参考了

https://blog.csdn.net/xu962336414/article/details/84032693

https://blog.csdn.net/qq_34144916/article/details/81154528?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase