Java 在列表中间插入的情况下,LIKEDLIST真的比ARARYLIST快吗?

Java 在列表中间插入的情况下,LIKEDLIST真的比ARARYLIST快吗?,java,collections,Java,Collections,-LinkedList和ArrayList之间有什么区别?何时最好使用链接列表 我认为每个Java开发人员在采访中都至少听过一次这个问题 如果希望能够在列表中间插入项目,请使用链接表。 这是这个问题的常见答案。大家都知道。每次您询问列表实现之间的差异时,您都会得到如下答案: 我应该什么时候使用LinkedList?何时需要在元素之间或在开始时高效地删除 忘了提到插入成本。在LinkedList中,一旦找到正确的位置,插入成本将O(1),而在ArrayList中,插入成本将上升到O(n)-所有元

-
LinkedList
ArrayList
之间有什么区别?何时最好使用
链接列表

我认为每个Java开发人员在采访中都至少听过一次这个问题

如果希望能够在列表中间插入项目,请使用链接表。

这是这个问题的常见答案。大家都知道。每次您询问列表实现之间的差异时,您都会得到如下答案:

我应该什么时候使用LinkedList?何时需要在元素之间或在开始时高效地删除

忘了提到插入成本。在LinkedList中,一旦找到正确的位置,插入成本将
O(1)
,而在ArrayList中,插入成本将上升到
O(n)
-所有元素必须移动到插入点之外

当您希望能够将项目插入列表中间(例如优先级队列)时,链接列表优于数组。

ArrayList速度较慢,因为它需要复制阵列的一部分,以便删除已变为空闲的插槽。LinkedList只需操纵几个引用

还有更多

但是你有没有试过自己复制它?我昨天试过了,结果如下:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Test {
    public static void main(String... args) {
        final int MAX_VAL = 10000;
        List<Integer> linkedList = new LinkedList<Integer>();
        List<Integer> arrayList = new ArrayList<Integer>();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
            arrayList.add(i);
        }
        long time = System.nanoTime();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(MAX_VAL/2, i);
        }
        System.out.println("LL time: " + (System.nanoTime() - time));
        time = System.nanoTime();
        for(int i = 0; i < MAX_VAL; i++) {
            arrayList.add(MAX_VAL/2, i);
        }
        System.out.println("AL time: " + (System.nanoTime() - time));
    }
}
import java.util.ArrayList;
导入java.util.LinkedList;
导入java.util.List;
公开课考试{
公共静态void main(字符串…参数){
最终整数最大值=10000;
List linkedList=新建linkedList();
List arrayList=新建arrayList();
对于(int i=0;i
输出:

LL时间:114098106

美国时间:2412189

那是什么呢?为什么LinkedList如此糟糕?也许我们应该尝试删除操作而不是添加?好的,让我们试试:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Test {
    public static void main(String... args) {
        final int MAX_VAL = 10000;
        List<Integer> linkedList = new LinkedList<Integer>();
        List<Integer> arrayList = new ArrayList<Integer>();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
            arrayList.add(i);
        }
        long time = System.nanoTime();
        for(int i = 0; i < MAX_VAL/2; i++) {
            linkedList.remove(MAX_VAL/2);
        }
        System.out.println("LL time: " + (System.nanoTime() - time));
        time = System.nanoTime();
        for(int i = 0; i < MAX_VAL/2; i++) {
            arrayList.remove(MAX_VAL/2);
        }
        System.out.println("AL time: " + (System.nanoTime() - time));
    }
}
import java.util.ArrayList;
导入java.util.LinkedList;
导入java.util.List;
公开课考试{
公共静态void main(字符串…参数){
最终整数最大值=10000;
List linkedList=新建linkedList();
List arrayList=新建arrayList();
对于(int i=0;i
输出:

LL时间:27581163

美国时间:3103051

哦,ArrayList仍然比LinkedList快。原因是什么?这个神话破灭了吗?也许我错了

破坏

不是真的。这里

for(int i = 0; i < MAX_VAL; i++) {
    linkedList.add(MAX_VAL/2, i);
}
for(int i=0;i
您不只是插入项目;每次从一开始迭代到
i
,您都要为此付出代价。当然,这是
O(i)

另一方面,在您真正了解中间列表插入的性能优势之前,列表必须非常大
System.arraycopy
是一个非常快速的操作,在另一端,每次插入到
LinkedList
中都需要分配一个节点实例

总之,
ArrayList
对于99%或更多的实际案例是一个更好的选择,利用
LinkedList
的狭隘优势需要非常小心

关于JVM微基准标记的一般说明 我还应该警告您,您的基准测试代码存在严重缺陷。在JVM上进行微博客共享时,需要注意的事情很多,例如:

  • 始终对代码进行预热,让JIT编译器处理它
  • 由于准确性/精确性问题,在解释
    nanoTime
    结果时要非常小心。使读数至少增加到毫秒(百万纳秒),以确保可靠性
  • 控制垃圾收集器的虚假副作用
  • 等等

因此,建议使用现成的微基准标记框架,例如。

必须注意以下简单的评测:

  • 垃圾收集可能在不可预测的时间发生,从而减慢了速度 不可预测的部分
  • JRE在第一次启动时速度较慢,之后会“预热”
为了解决这个问题,在循环中进行分析,以随机顺序多次重复这两种情况,并取典型值而不是极值。这有时会产生不同的结果。

为了演示add()操作的(in)效果,最好使用对象而不是列表对象。如果直接在链表上使用add()方法,它将从列表头开始,并且必须迭代到要插入项的位置。这部分取O(n)。如果您使用ListIterator,它将保持我们添加元素的位置,并且算法不必每次迭代到列表的中间

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

public class Test {
    public static void main(String... args) {
        final int MAX_VAL = 10000;
        List<Integer> linkedList = new LinkedList<Integer>();
        List<Integer> arrayList = new ArrayList<Integer>();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
            arrayList.add(i);
        }
        long time = System.nanoTime();


        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(MAX_VAL/2, i);
        }
        System.out.println("LL time:\t" + (System.nanoTime() - time));

        time = System.nanoTime();
        for(int i = 0; i < MAX_VAL; i++) {
            arrayList.add(MAX_VAL/2, i);
        }
        System.out.println("AL time:\t" + (System.nanoTime() - time));


        //Reset the lists
        linkedList = new LinkedList<Integer>();
        arrayList = new ArrayList<Integer>();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
            arrayList.add(i);
        }

        time = System.nanoTime();
        ListIterator<Integer> li = linkedList.listIterator(MAX_VAL/2);
        for(int i = 0; i < MAX_VAL; i++) {
            li.add(i);
        }
        System.out.println("LL iterator:\t" + (System.nanoTime() - time));

        time = System.nanoTime();
        ListIterator<Integer> ali = arrayList.listIterator(MAX_VAL/2);
        for(int i = 0; i < MAX_VAL; i++) {
            ali.add(i);
        }
        System.out.println("AL iterator:\t" + (System.nanoTime() - time));
    }
}

因为ArrayList按顺序存储值 因此 1:更快地添加值(只需添加最后一个索引中的值) 2:更新或删除速度较慢(将有
LL time:     237819474
AL time:      31410507
LL iterator:   5423172
AL iterator:  23975798
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;

public class TestList {

    public static void main(String... args) {
        final int MAX_VAL = 10000;
        int[] currentIndex = {0, 0, 0, 0};
        int[] remaining = {50, 50, 50, 50};
        int[][] sequence = new int[4][50];

        while (keepWorking(remaining)) { //run 50 tests for each case at random

            int currentMethod = chooseMethod(remaining); //choose case. Probability is higher for tests with less trials

            switch (currentMethod) { //run a test based on the choice
                case 0:
                    sequence[currentMethod][currentIndex[currentMethod]] = getLL(MAX_VAL);
                    break;
                case 1:
                    sequence[currentMethod][currentIndex[currentMethod]] = getAL(MAX_VAL);
                    break;
                case 2:
                    sequence[currentMethod][currentIndex[currentMethod]] = getLLIt(MAX_VAL);
                    break;
                default:
                    sequence[currentMethod][currentIndex[currentMethod]] = getALIt(MAX_VAL);
                    break;
            }

            remaining[currentMethod]--;
            currentIndex[currentMethod]++;
        }

        for (int[] ar : sequence) {
            Arrays.sort(ar);
        }

        System.out.println("Time (us\nLL    \tAL\tLL incr\t AL incr");
        for (int i = 0; i < sequence[0].length; i++) {
            System.out.println(sequence[0][i] + "\t" + sequence[1][i] + "\t" + sequence[2][i] + "\t" + sequence[3][i]);
        }
        System.out.println("\nTime normalized to fastest run of a method\nLL\tAL\tLL incr\t AL incr");
        for (int i = 0; i < sequence[0].length; i++) {
            System.out.print(i);
            for (int j = 0; j < sequence.length; j++) {  //to 4
                int a = sequence[j][i] / (sequence[j][0]/100); //to keep result within the scope of int
                System.out.print("\t" + a);
            }
            System.out.println();
        }
    }

    public static boolean keepWorking(int[] remaining) {

        for (int i = 0; i < remaining.length; i++) {
            if (remaining[i] > 0) {
                return true;
            }
        }
        return false;
    }

    public static int chooseMethod(int[] rem) {
        int[] bins = new int[rem.length];
        for (int i = 0; i < rem.length; i++) {
            for (int j = i; j < rem.length; j++) {
                bins[j] += rem[i];
            }
        }
        int randomNum = new Random().nextInt(bins[rem.length - 1]);
        for (int i = 0; i < bins.length; i++) {
            if (randomNum < bins[i]) {
                return i;
            }
        }
        return -1;
    }

    public static int getLL(int MAX_VAL) {

        List<Integer> linkedList = new LinkedList<>();
        for (int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
        }
        long time = System.nanoTime();

        for (int i = 0; i < MAX_VAL; i++) {
            linkedList.add(MAX_VAL / 2, i);
        }
        return (int) (System.nanoTime() - time)/1000;
    }

    public static int getAL(int MAX_VAL) {

        List<Integer> arrayList = new ArrayList<>(MAX_VAL);
        for (int i = 0; i < MAX_VAL; i++) {
            arrayList.add(i);
        }
        long time = System.nanoTime();
        for (int i = 0; i < MAX_VAL; i++) {
            arrayList.add(MAX_VAL / 2, i);
        }
        return (int) (System.nanoTime() - time)/1000;
    }

    public static int getLLIt(int MAX_VAL) {

        List<Integer> linkedList = new LinkedList<>();
        for (int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
        }

        long time = System.nanoTime();

        ListIterator<Integer> li = linkedList.listIterator(MAX_VAL / 2);
        for (int i = 0; i < MAX_VAL; i++) {
            li.add(i);
        }
        return (int) (System.nanoTime() - time)/1000;
    }

    public static int getALIt(int MAX_VAL) {

        List<Integer> arrayList = new ArrayList<>(MAX_VAL);
        for (int i = 0; i < MAX_VAL; i++) {
            arrayList.add(i);
        }

        long time = System.nanoTime();
        ListIterator<Integer> ali = arrayList.listIterator(MAX_VAL / 2);
        for (int i = 0; i < MAX_VAL; i++) {
            ali.add(i);
        }
        return (int) (System.nanoTime() - time)/1000;
    }
}