java 为什么ArrayDeque比LinkedList更好

uurity8g  于 2023-04-10  发布在  Java
关注(0)|答案(9)|浏览(193)

我试图理解为什么Java的ArrayDeque比Java的LinkedList更好,因为它们都实现了Deque接口。
我几乎没有看到有人在他们的代码中使用ArrayDeque。如果有人更多地阐明ArrayDeque是如何实现的,那将是有帮助的。
如果我理解了它,我会更有信心使用它。我不能清楚地理解JDK实现,因为它管理头部和尾部引用的方式。

mzsu5hc0

mzsu5hc01#

链接结构可能是最不适合在每个元素上进行缓存未命中迭代的结构。除此之外,它们还消耗更多的内存。
如果你需要添加/删除两端,ArrayDeque明显优于链表。随机访问每个元素对于循环队列也是O(1)。
链表唯一更好的操作是在迭代过程中删除当前元素。

iqxoj9l9

iqxoj9l92#

我认为LinkedList的主要性能瓶颈是,无论何时推到deque的任何一端,在后台实现都会分配一个新的链表节点,这基本上涉及JVM/OS,而且代价很高。此外,无论何时从任何一端弹出,LinkedList的内部节点都有资格进行垃圾收集,这在后台需要更多的工作。由于链表节点被分配在不同的地方,CPU缓存的使用不会带来太多的好处。
如果你感兴趣的话,我有一个证明,向ArrayListArrayDeque添加(追加)一个元素是在摊销常数时间内运行的;请参阅this

rkttyhzu

rkttyhzu3#

所有批评LinkedList的人,想想其他在Java中使用List的人可能大多数时候都在使用ArrayListLinkedList,因为它们在Java 6之前就已经存在了,而且大多数书籍都是以这些为起点的。
但是,这并不意味着我会盲目地站在LinkedListArrayDeque一边。如果你想知道,看看下面的基准测试done by Brian(存档)。
测试设置考虑:

  • 每个测试对象都是一个500个字符的字符串。每个字符串都是内存中不同的对象。
  • 测试阵列的大小将在测试期间变化。
  • 对于每个阵列大小/队列实现组合,运行100个测试,并计算每个测试的平均时间。
  • 每个测试都包括用所有对象填充每个队列,然后将它们全部删除。
  • 以毫秒为单位测量时间。

测试结果:

  • 在10,000个元素以下,LinkedList和ArrayDeque测试的平均值低于1毫秒。
  • 随着数据集变大,ArrayDeque和LinkedList平均测试时间之间的差异也会变大。
  • 在测试大小为9,900,000个元素时,LinkedList方法比ArrayDeque方法长165%。

图表:

外卖:

  • 如果您的需求是存储100或200个元素,那么使用这两种队列都不会有太大的区别。
  • 但是,如果您正在移动的上进行开发,则可能需要使用ArrayListArrayDeque,并且由于严格的内存限制,可能需要对列表的最大容量进行很好的猜测。
  • 有很多代码是用LinkedList编写的,所以在决定使用ArrayDeque时要小心,特别是因为它不实现List接口(我认为这是一个足够大的理由)。可能是你的代码库与List接口进行了广泛的对话,最有可能的是,您决定使用ArrayDeque。将其用于内部实现可能是个好主意...
vaj7vani

vaj7vani4#

ArrayDequeLinkedList实现了Deque接口,但实现方式不同。
请参阅Oracle documentation了解更多详细信息。
主要区别:

  1. ArrayDeque* 类是 Deque 接口的可调整大小的数组实现,LinkedList 类是列表实现
  2. NULL元素可以添加到 LinkedList,但不能添加到 ArrayDeque
    1.ArrayDequeLinkedList 在两端的添加和删除操作更有效,LinkedList实现在迭代期间删除当前元素更有效
    1.LinkedList 实现比 ArrayDeque 消耗更多内存
    因此,如果你不需要支持NULL元素,并且希望内存更少,并且在两端添加/删除元素的效率更高,那么 ArrayDeque 是最好的选择
g6baxovj

g6baxovj5#

ArrayDeque是Java 6中的新特性,这就是为什么很多代码(尤其是试图与早期Java版本兼容的项目)不使用它的原因。
在某些情况下,它“更好”,因为您没有为每个要插入的项分配一个节点;相反,所有元素都存储在一个巨大的数组中,如果它变满了,就会调整大小。

ubbxdtey

ubbxdtey6#

我不认为ArrayDequeLinkedList好,它们是不同的。
ArrayDeque的平均速度比LinkedList快。但是对于添加元素,ArrayDeque需要摊销常数时间,LinkedList需要常数时间。
对于时间敏感的应用程序,要求所有操作都需要恒定的时间,只能使用LinkedList
ArrayDeque的实现使用数组,需要调整大小,偶尔,当数组已满,需要添加一个元素时,调整大小将花费线性时间,导致add()方法花费线性时间。如果应用程序对时间非常敏感,这可能是一场灾难。
关于Java实现这两种数据结构的更详细的解释可以在普林斯顿大学Coursera上的“Algorithms, Part I”课程中找到,该课程由韦恩和Sedgewick教授。该课程对公众免费。
在“第2周”的“堆栈和队列”部分的视频“调整阵列大小”中解释了详细信息。

yrdbyhpb

yrdbyhpb7#

虽然**ArrayDeque<E>LinkedList<E>都实现了Deque<E>接口,但ArrayDeque基本上是用Object数组E[]**来保存Object内部的元素,所以一般用index来定位头尾元素。
总而言之,它的工作原理和Deque类似(使用了所有Deque的方法),但是使用了数组的数据结构。至于哪一种更好,取决于你如何使用它们以及在哪里使用它们。

swvgeqrz

swvgeqrz8#

并不总是这样。
例如,在下面的例子中,根据leetcode 103,linkedlistArrayDeque具有更好的性能。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> rs=new ArrayList<>();
        if(root==null)
            return rs;
        // 👇 here ,linkedlist works better
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        boolean left2right=true;
        while(!queue.isEmpty())
        {
            int size=queue.size();
            LinkedList<Integer> t=new LinkedList<>();
            while(size-->0)
            {
                TreeNode tree=queue.remove();
                if(left2right)  
                    t.add(tree.val);
                else
                    t.addFirst(tree.val);
                if(tree.left!=null)
                {
                    queue.add(tree.left);
                }
                if(tree.right!=null)
                {
                    queue.add(tree.right);
                }
            }
            rs.add(t);
            left2right=!left2right;
        }
        return rs;
    }
}
3lxsmp7m

3lxsmp7m9#

时间复杂度为O(1),而时间复杂度为O(N)。ArrayDeque不是线程安全的,所以需要手动同步,这样你就可以通过多个线程访问它,所以它们更快。

相关问题