【问题标题】:Why do some languages like C++ and Java have a built-in LinkedList datastructure? [closed]为什么某些语言(如 C++ 和 Java)具有内置的 LinkedList 数据结构? [关闭]
【发布时间】:2018-10-22 22:58:56
【问题描述】:

许多编程语言都提供了内置的数据结构,尽管已知一些数据结构,例如 链表 很容易实现,因此,语言通常不将其作为内置的-在数据结构中。 一些语言,如C++ has(如std::list,双向链接)、as well as Java(如LinkedList<T>,双向链接)。

例如,Python 已经在其库中内置了多个数据结构,包括列表、元组、集合、字典(甚至不需要导入),以及来自 collections 库的数据结构。它没有内置的 LinkedList 数据结构,因为在 Python 中实现自己的 LinkedList 已经很容易了。

请注意,Python 列表的底层数据结构实际上是一个数组,正如"What is the underlying data structure for Python lists?" 中所回答的那样。此外,collections 中的 deque 是一个双端 queue,它应该缺少一些功能,因为它通常不支持在中间进行索引或插入(但是,自 3.5 起已添加索引功能,带有 indexinsert,但在 Python 2 中不存在)。

似乎提供内置的LinkedList 数据结构是多余且不必要的。为什么其他一些编程语言库(例如 C++ 和 Java 库)包含LinkedList 数据结构,即使它们很容易实现?如果是这样,用这些语言实现自己的链表有什么风险?

【问题讨论】:

  • 我相信 Guido 有一篇关于这个的帖子,可能在他的 Python History 博客上,但我找不到它。 IIRC,关键是链表有很多不同的选项(单链还是双链?节点作为列表还是作为列表处理?可变还是不可变?尾部共享?),它们都很容易在 Python 中构建,在 C 中实现它们并没有真正的速度或空间优势(与数组或哈希表不同),而且它们中没有一个比其他需要内置的更有用。
  • 链接列表通常是一个可怕的想法。他们的主要声名是它们实施起来很简单。特别是,大多数现实世界的链表都是临时侵入式链表,而不是类似容器的链表。
  • @abarnert 你能找到这个吗?这对于提供正确的答案非常有用。
  • @abarnert 我一直在考虑编写一个推测性的答案,将您的评论与来自各种语言的链表 API 文档中的引号相结合,大约是 Almost always it is better to use *our contigous resizable array* or *our deque* instead of *our linked list*. In general, array-based containers are faster, more memory efficient and make better use of CPU cache. 虽然我有点新,所以我不确定在实际答案中对这种猜测的不满程度。
  • @Cosmo 标准的答案是,你几乎总是想要一棵宽树或一根长绳,以便两全其美……除了实际上,你不需要扩展到 30 亿元素并且仍然在中间拼接,所以你只需要一个数组。 Raymond Hettinger 在the blist pep 上的 cmets 可能与此相关。

标签: java python c++ data-structures linked-list


【解决方案1】:

我搜索了 Guido 的 Python History 博客,因为我确信他已经写过这方面的内容,但显然这不是他这样做的地方。因此,这是基于推理(也就是有根据的猜测)和记忆(可能有问题)的结合。

让我们从头开始:在不知道为什么 Guido 没有在 Python 0.x 中添加链表的情况下,我们至少知道为什么核心开发人员从那时起就没有添加它们,即使他们已经添加了一堆从OrderedDictset 的其他类型?

是的,我们有。简短的版本是:二十多年来,没有人要求它。多年来添加到内置函数或标准库中的几乎所有内容(变体)在PyPIActiveState recipes 上被证明是有用和流行的。这就是 OrderedDictdefaultdict 的来源,例如,enumdataclass(基于 attrs)。还有一些其他容器类型的流行库——排序的 dict/set、OrderedSet、树和尝试等的各种排列,SortedContainersblist 都被提议包含在标准库中,但被拒绝了。

但是没有流行的链表库,这就是为什么它们永远不会被添加。

那么,这又把问题带回了一步:为什么没有流行的链表库?

首先,“链表”并不是真正的一种类型,而是一整套类型——单链与双链,节点作为列表与句柄作为列表,是否共享尾部,懒惰(大概通过@property-style 触发器)或严格......以及循环列表等变体。当然,这些中的每一个都远不如整个家庭有用。而且你不能在接口或它们的实现之间共享这么多——处理 Lisp 风格的 cons 列表的接口对于像 C++ std::list 这样的东西根本没有意义。

其次,所有不同的变体都非常容易构建。 OrderedDict 进入语言的原因是人们能够证明它实际上是非常棘手的。集合很容易正确,但如果不借用 dicts 的幕后实现细节,就不可能进行空间优化。链表很容易得到正确和优化。如果你想要一个,任何品种,你在几分钟内建立一个,你就完成了。 (事实上​​,OrderedDict 在后台使用了一个……)

同时,自 Python 2.3,尤其是自 3.0 以来,迭代器协议已成为一切的中心范式。其他语言借用 Python 的生成器是有原因的——但在大多数这些语言中,它们并没有提供相同的好处,因为整个语言并不是围绕它们构建的。能够对任何集合进行前向迭代——包括一个甚至不存在的“虚拟”集合——已经为您提供了惰性尾部共享缺点列表的大部分优点,而无需一个。另一方面,它缺少的主要内容是方便的恒定时间(变异)拼接——但缺少的东西实际上很难适应迭代器范式。 (请参阅itertools 了解如何做到这一点——毕竟只是teechain——以及这样做感觉多么笨拙。)

(当然,更复杂的迭代协议也有优势,比如 C++ 使用的,或者更好的是 Swift。双向和可变迭代与不可变迭代相比,双向链表可能更有用. 但是在功能和简单性之间需要权衡,Python 很久以前就做出了选择。)

最后,但同样重要的是,链表,尤其是 cons 样式的尾共享链表,是一种固有的递归数据结构。 Python 的哲学是仅当您有固有的递归问题时才使用递归。 Guido 认为这些很少,这就是 Python 不这样做的原因,例如尾递归消除。他不想在他的语言中使用mapreduce,但是一旦他弄清楚如何迭代而不是递归地执行它们,他就愿意让它们留下来。并且存储线性数据并不是一个固有的递归问题。树?当然,它们几乎必须是递归的。但是列表?没有。

【讨论】:

    猜你喜欢
    • 2017-07-31
    • 2019-02-18
    • 2013-01-18
    • 2018-06-19
    • 2018-08-10
    • 2020-06-26
    • 2021-03-24
    • 2021-11-13
    • 1970-01-01
    相关资源
    最近更新 更多