【问题标题】:Why is there not a LinkedList.join() method to merge two unsorted lists in O(1) time?为什么没有 LinkedList.join() 方法可以在 O(1) 时间内合并两个未排序的列表?
【发布时间】:2021-08-08 15:41:41
【问题描述】:

我不是数据结构专家,但据我了解,链表在大多数情况下被证明效率很低,主要是由于缓存未命中。我曾经考虑使用它们的少数原因之一是具有在恒定时间内合并/拆分其中两个的独特能力;但是该类没有提供这样的方法。

我想,好吧,我会实现自己的以满足我的需求。我希望新类遵循 Collections 框架 API,所以我让它实现和扩展与标准 LinkedList 完全相同的类。很快我意识到我只是在重新创建 LinkedList,所有这些都是毫无意义的。那时我还不如复制粘贴代码并添加我需要的方法。

同样,我找不到为什么没有这种方法的原因(除了我还远未完全掌握的线程安全性)。一切看起来都像是一个标准的链表实现,并且类似于 C++ STL 中的splice() 的方法可以放入其中。

我错过了什么吗?

LinkedList<Integer> list1 = new LinkedList();
list1.add(4); 
list1.add(2);
list1.add(7);
// list1 = {4 , 2 , 7}

LinkedList<Integer> list2 = new LinkedList();
list2.add(9);
list2.add(4);
list2.add(6);
// list2 = {9 , 4 , 6}

list1.join(list2); // in O(1)
// at this point list2 should be invalidated/empty
// list1 = {4 , 2 , 7 , 9 , 4 , 6}
// list2 = {}

【问题讨论】:

  • 效率高于一切?绝对不。可读性和可用性至少同样重要,尤其是对于 Java。而且您描述的操作甚至没有您想象的那么频繁(并且做“错误”的方式可能没有您想象的那么慢)。我不知道你对 HashTable 是什么意思。请注意,即使LinkedList 的作者也不会使用它(source)。 ArrayList straight up behaves better in most circumstances.
  • 您是否发现LinkedList 中利用链表优势的任何 方法?
  • 没错。更改头部不是主要优势,任何其他不在头部或尾部的更新都需要 O(n) 遍历。更糟糕的是,当通过不同的迭代器进行结构更改时,可以记住对 O(1) 插入或删除节点的引用的列表迭代器会失效。因此,维护两个或多个列表迭代器以在不遍历的情况下将元素从一个位置转移到另一个位置是行不通的。并且没有用于任何 O(1) 链接操作的专用 API。
  • @fvalasiad - 正如您所评论的,您可以创建自己的单链表或双链表实现,使用对节点的引用代替迭代器,以及几乎重复的 C++ std::list 功能.
  • 我想说,LinkedList 更像是一个概念验证,以证明可以以不同于 ArrayList 的方式实现 List(在JDK 1.2 的 Collection API 只有基于数组的Vector)。 This statement作者的都说了……

标签: java collections linked-list doubly-linked-list


【解决方案1】:

Java 的原生链表实现存在问题。无法在列表内或列表之间移动节点,不等同于 C++ std::list::splice()。如果在链表中的任何位置插入或删除节点,则链表的所有迭代器都将失效,但使用迭代器参数完成删除的情况除外,在这种情况下,除了用于执行插入或删除无效。迭代器不能被浅拷贝,赋值只是将目标迭代器指向源迭代器指向的同一个迭代器对象。

这些问题使合并或合并排序等操作变得困难,对于基于迭代器的操作更是如此。

在 Visual Studio C++ std::list 的情况下,删除节点只会使删除节点的迭代器无效,并且仅在调试版本中。在发布版本中,没有迭代器失效,程序员需要确保删除的节点不会导致迭代器指向已删除的对象。


至于为什么 Java 的原生链表有这些限制,Sun 当时声明的原因是他们想要一个“紧凑的框架”而不是与 C++ 保持一致,后者比 Java 集合早了 4 年(1998 年用于 Java 集合,1994 年 HP 版权在 Visual Studio 2005 和 Microsoft 和其他 C++ 编译器的早期版本的情况下,大多数或所有 STL 包含文件的末尾显示的日期)。尽管集合的前身之一 JGL 确实有与 C++ 保持一致的目标。

https://en.wikipedia.org/wiki/Java_collections_framework#History

还有其他对 Java 的批评,例如没有无符号整数、不同地对待基元和对象、对基元的操作集有限、...

【讨论】:

  • 我不明白这如何回答为什么的问题。
  • @JoachimSauer - 我更新了我的答案以包括 Sun 的选择,但我质疑“紧凑框架”是否是放弃功能的正当理由,特别是当预收集实现之一 JGL,目标是与 C++ 和它的 STL 保持一致。
  • “除非使用迭代器完成插入或删除”,否则您不能使用Iterator 进行插入。只删除。用ListIterator,也可以设置,但是还是不能插入。
  • @AndyTurner - 还有一个限制。我仍然不明白他们决定这样做的“原因”,因为之前的 JGL 已经“正确”地实现了它。我更新了我的答案。
猜你喜欢
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 2016-10-22
  • 2017-01-08
  • 1970-01-01
  • 2021-08-12
相关资源
最近更新 更多