当前位置: 首页 > 知识库问答 >
问题:

ArrayList相对于LinkedList的优势及性能比较

乌和畅
2023-03-14

最近我一直在寻找Java的LinkedLists。经过一些研究,我发现使用LinkedList而不是ArrayList的主要好处是,在包含大量数据的列表中删除/添加元素时,它会更快。因此,使用ArrayList而不总是使用LinkedList会有什么好处呢?

为了比较起见,我编写了这个简单的程序来比较LinkedList和ArrayList之间的运行速度。该程序的概念是,它创建一个长度为n的int数组,并用从integer.min_valueinteger.max_value的随机整数填充该数组。

然后,它创建另一个数组,它是第一个数组元素的x%。例如,如果第一数组是[1,2,3,4,5,6,7,8,9,10],则如果x被设置为50%,则第二数组可以是[2,4,5,9,10]。这将产生一个从原始数组的开始,中间和结束都带有随机元素的数组。

在创建这些数组期间所经过的运行时间不计算在内。然后,程序创建一个LinkedList,将第一个数组中的所有元素添加到这个列表中,并从这个列表中删除第二个数组中的元素。我重复这个过程y次,并使用System.nanoTime()计算平均经过的时间。

我知道我的代码远未优化,但由于它是LinkedList和ArrayList完全相同的代码,只更改正在创建的列表类型,而不预先设置初始化时列表的大小,结果应该不会受到影响,对吗?例如,如果我的错误代码使它的运行速度慢了400%,因为两者都使用相同的代码,结果应该不会受到任何影响,因为理论上两者都慢了400%。

我发现的结果是,ArrayList平均比LinkedList快360%,至少在使用10万整数数组并运行10次同时删除45%元素的情况下是这样。

通过阅读这篇,这篇和这篇StackOverflow文章来比较ArrayList和LinkedList的性能,我了解到,发生这种情况是因为LinkedList为每个节点分配了更多的内存(24字节vs ArrayList上的4字节)。虽然,理论上,LinkedList在数组元素中间添加/删除元素时速度更快,平均只需要O(n/4)步,而List则需要O(n/2)步,但在现实中--至少在我的测试中,以及在上面链接的帖子中执行的测试中--这似乎不是真的。如果是这样,那么问题就反过来了:使用LinkedList而不使用ArrayList有什么好处?只能使用list.iterator()

下面是我得到的确切结果和我用来得到它们的代码:

It took 53079,970300 ms with LinkedList to run 10 times with an array of 100000 integers removing 45 percent of the elements
The average run time for LinkedList was 5307,997030 ms
It took 14344,833900 ms with normal List to run 10 times with an array of 100000 integers removing 45 percent of the elements
The average run time for normal List was 1434,483390 ms
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class LinkedLists {
    public static void main(String[] args) {

        int   timesToRun          = 10;
        int   percentageToRemove  = 45;
        int[] arr                 = getRandomArray(100_000);
        int[] integersToRemoveArr = getIntegersToRemoveArr(percentageToRemove, arr);

        long elapsedTime = 0L;
        long startTime;
        long endTime;

        //LinkedLists
        startTime = System.nanoTime();

        for (int i = 0; i < timesToRun; i++) {
            linkedList(arr, integersToRemoveArr);
        }

        endTime = System.nanoTime();
        elapsedTime += endTime - startTime;
        System.out.printf("It took %f ms with LinkedList to run %d times with an array of %d integers removing %d percent of the elements\n", (endTime - startTime) * 0.000001, timesToRun, arr.length, percentageToRemove);
        System.out.printf("The average run time for LinkedList was %f ms\n", elapsedTime/timesToRun * 0.000001);

        //Force GC - again, not sure I need this, just to be safe
        Runtime.getRuntime().gc();

        //Normal Lists
        elapsedTime = 0L;
        startTime = System.nanoTime();

        for (int i = 0; i < timesToRun; i++) {
            normalList(arr, integersToRemoveArr);
        }

        endTime = System.nanoTime();
        elapsedTime += endTime - startTime;

        System.out.printf("It took %f ms with normal List to run %d times with an array of %d integers removing %d percent of the elements\n", (endTime - startTime) * 0.000001, timesToRun, arr.length, percentageToRemove);
        System.out.printf("The average run time for normal List was %f ms\n", elapsedTime/timesToRun * 0.000001);


    }

    /**
     * Get an array wit X percent of the integers of the providade array
     */
    public static int[] getIntegersToRemoveArr(int percentageToRemove, int[] originalArray) {

        if (percentageToRemove > 100 || percentageToRemove < 0) {
            return new int[]{};
        }

        List<Integer> indexesToRemove = new ArrayList<>();
        while (indexesToRemove.size() < (originalArray.length * (percentageToRemove)/100F)) {
            int x = ThreadLocalRandom.current().nextInt(0, originalArray.length-1);
            indexesToRemove.add(originalArray[x]);
        }

        int[] arr = new int[indexesToRemove.size()];

        for (int i = 0; i < arr.length; i++) {
            arr[i] = indexesToRemove.get(i);
        }

        Arrays.sort(arr);

        return arr;

    }

    public static int[] getRandomArray(int length) {
        if (length < 1) {
            return new int[]{};
        }

        int[] arr = new int[length];

        for (int i = 0; i < length; i++) {
            arr[i] = ThreadLocalRandom.current().nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
        }

        return arr;
    }

    /**
     * Create a LinkedList from the provided array and remove from the list the elements in integersToRemove
     */
    public static void linkedList(int[] arr, int[] integersToRemove) {
        LinkedList<Integer> list = new LinkedList<>();

        for (int i : arr) {
            list.add(i);

    }
        for (int i : integersToRemove) {
            list.remove((Integer) i);
        }

        //Nulling the element to allow GC to remove it - i'm not sure this is needed since I'm not using
        //this var anywhere, but just to be safe
        list = null;

    }

    /**
     * Create a LinkedList from the provided array and remove from the list the elements in integersToRemove
     */
    public static void normalList(int[] arr, int[] integersToRemove) {
        List<Integer> list = new ArrayList<>();

        for (int i : arr) {
            list.add(i);
        }

        for (int i : integersToRemove) {
            list.remove((Integer) i);
        }

        //Nulling the element to allow GC to remove it - i'm not sure this is needed since I'm not using
        //this var anywhere, but just to be safe
        list = null;

    }
}

共有1个答案

施华奥
2023-03-14

好处是您有O(1)Add。尽管ArrayList平均速度更快,但当您add()并需要扩展容量时,它可能会非常慢。对于延迟关键的应用程序,这可能很重要。另外,由于它是一个双重链表,所以前置项比大型ArrayList要快得多,您没有测试过大型ArrayList。

 类似资料:
  • 本文向大家介绍HTML5相比于HTML4有哪些优势?相关面试题,主要包含被问及HTML5相比于HTML4有哪些优势?时的应答技巧和注意事项,需要的朋友参考一下 1.更强的语义化 2.更丰富的功能,比如 3.更简洁的模板语法

  • 问题内容: 按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实,参考或专业知识的支持,但是这个问题可能会引起辩论,争论,民意调查或扩展讨论。如果您认为此问题可以解决并且可以重新提出,请访问帮助中心以获取指导。 7年前关闭。 与MySQL相比,使用MySQLi有什么优势? 问题答案: 见文档: PHP的mysqli扩展是什么? mysqli扩展或MySQL改进的扩展是为了利用MySQ

  • 问题内容: 我了解这是作为双重链接列表实现的。它在add和remove上的性能优于,但在get和set方法上却较差。 这是否意味着我应该选择在插入? 我写了一个小测试,发现插入速度更快。那如何链表比? 请参考下面的示例。 问题答案: Linkedlist确实在插入时速度更快,问题出在您的示例中。在您的代码中,您一直都需要附加到末尾。对于ArrayList,它与LinkedList一样容易。您应该做

  • 本文向大家介绍HTML5相对于HTML4有哪些优势?相关面试题,主要包含被问及HTML5相对于HTML4有哪些优势?时的应答技巧和注意事项,需要的朋友参考一下 HTML5的规范都是基于用户优先准则来编写的,贴合开发者的编码习惯,语法限制不严,代码也更为精简,更易于阅读。 功能强大,用户体验佳 HTML5视频播放起来更流畅清晰,也更省电;HTML5游戏小巧流畅,画面质量高,操作易上手;HTML5广告

  • 我了解到是作为双链表实现的,它在添加和删除上的性能比好,但在get和set方法上的性能更差。 这是否意味着我应该选择而不是来插入? 我写了一个小测试,发现插入速度更快,那么链表怎么比快呢? 请参考下面我所做的例子。

  • 问题内容: 就像hibernate文档所说的那样,命名查询的目的是将HQL从项目中的不同位置清除到某个xml中的单个位置(在声明方法的情况下),这意味着在查询修改的情况下不需要重新编译,而是重新加载会话工厂这是必需的,这意味着在大多数情况下,由于查询对象被缓存,服务器将启动。但是在注释的情况下,我需要在实体级别定义命名查询。因此,这里再次需要编译。我的问题是命名查询在性能上是否也有帮助。这是我的理

  • 问题内容: 在一个字段只有5-10个不同的可能值的情况下使用枚举是否有性能优势?如果不是,优势是什么? 问题答案: 使用以下操作会导致巨大的性能 损失: 查询中的允许值列表,例如,填充一个下拉菜单。您必须从查询数据类型,并从返回的BLOB字段中解析列表。 更改允许值的集合。它需要一条语句,该语句锁定表并可以进行重组。 我不是MySQL的粉丝。我更喜欢使用查找表。另请参阅我对“ 如何在数据库中没有枚

  • 问题内容: 使用Executors而不是Java程序中的Thread有什么好处。 如 执行程序是否只是限制它允许一次运行的线程数(线程池)?它是否实际上将可运行对象多路复用到它创建的线程上?如果不是,那只是避免每次都必须写新的Thread(runnable).start()的一种方法? 问题答案: 是的,执行程序通常会将可运行对象多路复用到他们创建的线程上;他们将约束和管理一次运行的线程数;它们将