Java基础之ArrayList与LinkedList

在Java编程过程中,大家对ArrayList肯定不陌生,但是有没关注过ArrayList和LinkedList他们的区别呢?最后我们写的程序可能看似漂亮但是并不高效。那么我来分享一下ArrayList与LinkedList的区别。从数据结构上看,ArrayList是实现了基于动态数据的结构,而LinkedList是基于实现链表的数据结构。而两种数据结构在程序上提现出来的优缺点就在增删和改查的速率上。

数据的更新和查找

ArrayList的所有数据是在同一个地址上,而LinkedList的每个数据都有自己的地址,所以在数据进行查找的时候,由于LinkedList的每个数据地址不一样,get数据的时候ArrayList的速度会优于LinkedList,而更新数据的时候,虽然都是通过循环,循环到指定节点修改数据,但是LinkedList的查询速度已经是慢的,而且对于LinkedList而言,更新数据时不像ArrayList只需要找到对应的下标更新就好,LinkedList需要修改指针,速率不言而喻。

数据的增加和删除

对于数据的增加元素,ArrayList是通过移动该元素之后的元素位置,其后元素的位置全部+1,所以耗时较长,而LinkedList只需要将该元素前的后续指针指向该元素并将该元素的后续指针指向之后的元素即可。删除同理。

下面我们通过程序检验结果:

  • 针对增删
public class JavaDemo {
    public static final int N = 50000;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        getTime(new ArrayList());
        getTime(new LinkedList());
    }

    static void getTime(List list) {
        insertData(list);
        deleteData(list);
    }

    // 向list的指定位置插入N个元素,并统计时间
    private static void insertData(List list) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < N; i++)
            list.add(0, i);
        long endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println(getListName(list) + "插入" + N + "条数据耗时:" + interval + " ms");
    }

    // 从list的指定位置删除N个元素,并统计时间
    private static void deleteData(List list) {
        long startTime = System.currentTimeMillis();
        // 删除list第一个位置元素
        for (int i = 0; i < N; i++)
            list.remove(0);
        long endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println(getListName(list) + "删除" + N + "条数据耗时" + interval + " ms");
    }

    // 获取list类型名称
    private static String getListName(List list) {
        if (list instanceof LinkedList) {
            return "LinkedList";
        } else if (list instanceof ArrayList) {
            return "ArrayList";
        } else {
            return "error";
        }
    }
}

我得到的输出是:

ArrayList插入50000条数据耗时:169 ms
ArrayList删除50000条数据耗时160 ms
LinkedList插入50000条数据耗时:4 ms
LinkedList删除50000条数据耗时1 ms

从这个例子可以看出,当一个元素被加到ArrayList的最开端时,所有已经存在的元素都会后移,这就意味着数据移动和复制上的开销。相反的,将一个元素加到LinkedList的最开端只是简单的未这个元素分配一个记录,然后调整两个连接。在LinkedList的开端增加一个元素的开销是固定的,而在ArrayList的开端增加一个元素的开销是与ArrayList的大小成比例的。删除同理。然而如果在列表的末端添加数据呢?

    // 向list的末尾位置依次插入N个元素,并统计时间
    private static void insertData(List list) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < N; i++)
            list.add(i, i);
        long endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println(getListName(list) + "插入" + N + "条数据耗时:" + interval + " ms");
    }

我得到的输出是:

ArrayList插入50000条数据耗时:4 ms(ArrayList插入50000条数据耗时:5 ms)
LinkedList插入50000条数据耗时:4 ms

我在尝试多次的情况下,ArrayList会出现不同的结果,LinkedList都一直固定这个结果,但是从上面的结果可以看出,他们所耗的时间都差不多。

  • 针对改查
    // 从list中读取元素,并统计时间
    private static void readData(List list) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < N; i++) {
            list.get(i);
        }
        long endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println(getListName(list) + "查询" + N + "条数据耗时:" + interval + "ms");
    }

    // 从list的随机位置修改元素,并统计时间
    private static void updateData(List list) {
        long startTime = System.currentTimeMillis();
        int M = 40000;
        for (int i = 0; i < 40000; i++) {
            int j = (int) (1 + Math.random() * (40000 - 1 + 1));
            list.set(j, "list");
        }
        long endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println(getListName(list) + "随机修改" + M + "条数据耗时" + interval + " ms");
    }

我得到的输出是:

ArrayList查询50000条数据耗时:2ms
ArrayList随机修改40000条数据耗时4 ms
LinkedList查询50000条数据耗时:883ms
LinkedList随机修改40000条数据耗时825 ms

这个结果不是固定的,但是基本上ArrayList的时间要明显小于LinkedList的时间。对一个LinkedList做随机访问所消耗的时间与这个list的大小是成比例的。而相应的,在ArrayList中进行随机访问所消耗的时间是固定的。

总结

ArrayList和LinkedList的比较

  • ArrayList是实现了基于动态数组的数据结构,LinkedList基于双链表的数据结构。
  • 对于随机访问get数据,ArrayList是优于LinkedList,因为LinkedList要移动指针。
  • 有些说法认为LinkedList新增和删除比ArrayList更快,这种说法其实是不够严谨:
    • LinkedList做插入、删除的时候,慢在寻址,快在只需要改变前后Entry的引用地址。
    • ArrayList做插入、删除的时候,慢在数组元素的批量copy,快在寻址。

所以,如果待插入、删除的元素是在数据结构的前半段尤其是非常靠前的位置的时候,LinkedList的效率将大大快过ArrayList,因为ArrayList将批量copy大量的元素;越往后,对于LinkedList来说,因为它是双向链表,所以在第2个元素后面插入一个数据和在倒数第2个元素后面插入一个元素在效率上基本没有差别,但是ArrayList由于要批量copy的元素越来越少,操作速度必然追上乃至超过LinkedList。

从这个分析看出,如果你十分确定你插入、删除的元素是在前半段,那么就使用LinkedList;如果你十分确定你删除、删除的元素在比较靠后的位置,那么也可以考虑使用ArrayList。如果你不能确定你要做的插入、删除是在哪儿呢?那还是建议你使用LinkedList吧,因为一来LinkedList整体插入、删除的执行效率比较稳定,没有ArrayList这种越往后越快的情况;二来插入元素的时候,弄得不好ArrayList就要进行一次扩容,记住,ArrayList底层数组扩容是一个既消耗时间又消耗空间的操作。
另外大家要了解ArrayList与LinkedList的数据类型转换:
强制数据类型转换是不行的,因为类ArrayList与类LinkedList不是父子类关系。
但可以通过构造方法转换

        ArrayList arrayList = new ArrayList();
        //对arrayList对象添加数据
        LinkedList linkedList = new LinkedList(arrayList);
or
        LinkedList linkedList = new LinkedList();
        //对linkedList对象添加数据
        ArrayList<String> arrayList = new ArrayList(linkedList);

源码参考:
该文为本人学习的笔记,方便以后复习用到。总结是参考网上各大帖子,取其精华整合自己的理解而成。至于源码分析我就不贴了。另外记得这集合框架源码面试经常会问,大家要理解清楚。

这里写图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章