维护插入顺序的 Java 集合

Posted

技术标签:

【中文标题】维护插入顺序的 Java 集合【英文标题】:Java collections maintaining insertion order 【发布时间】:2011-04-11 06:50:19 【问题描述】:

为什么有些集合数据结构不保持插入顺序?与保持插入顺序相比,实现了什么特别的事情? 如果我们不维持秩序,我们会有所收获吗?

【问题讨论】:

例如,为什么java.util.HashSet 需要维护插入顺序? 不..我在问..我们在维持秩序的同时会失去任何东西吗..相反,如果我们不维持秩序,我们会有所收获 例如:LinkedList。想一想,在链表中追加/前置不是比在中间插入更容易吗? 【参考方案1】:

性能。如果你想要原始的插入顺序,有 LinkedXXX 类,它们在插入顺序中维护一个附加的链表。大多数时候你不关心,所以你使用 HashXXX,或者你想要一个自然顺序,所以你使用 TreeXXX。在这两种情况下,你为什么要支付链表的额外费用?

【讨论】:

ArrayList 的答案在哪里? @ADTC 它不适合答案。 嗯,ArrayList 使用数组支持来维护插入顺序,但我想性能比 LinkedXXX 类差? @ADTC 如果您不使用索引插入方法,它会维护插入顺序,或者对其进行排序,LinkedList 也是如此。 '有数组支持'是无关紧要的。 Javadoc 中描述了这两种情况下的性能。【参考方案2】:

集合不保持插入顺序。有些只是默认在最后添加一个新值。仅当您按其优先级或使用它以某种方式对对象进行排序时,维护插入顺序才有用。

至于为什么有些集合默认维护而其他不维护,这主要是由实现引起的,有时只是集合定义的一部分。

列表保持插入顺序,因为仅在末尾或开头添加新条目是 add(Object ) 方法的最快实现。

Sets HashSet 和 TreeSet 实现不维护插入顺序,因为对象被排序以便快速查找,维护插入顺序需要额外的内存。这会带来性能提升,因为插入顺序对 Set 几乎没有兴趣。

ArrayDeque deque 可用于简单的队列和堆栈,因此您希望具有“先进先出”或“先进后出”行为,两者都需要ArrayDeque 维护插入顺序。在这种情况下,插入顺序被维护为类合同的核心部分。

【讨论】:

【参考方案3】: hash tables 中固有地不维护插入顺序 - 这就是它们的工作方式(阅读链接到的文章以了解详细信息)。可以添加逻辑来维护插入顺序(如在LinkedHashMap 中),但这需要更多代码,并且在运行时需要更多内存和更多时间。性能损失通常并不显着,但也可能如此。 对于TreeSet/Map,使用它们的主要原因是SortedSet/Map接口中添加的自然迭代顺序和其他功能。

【讨论】:

顺便提一下:严格来说,Map 实现不是Collections,因为它们没有实现Collection 接口。他们确实有类似的方法,但仅此而已。检查:download.oracle.com/javase/1.4.2/docs/guide/collections/…(#Collection Interfaces)很可能OP的问题地址也映射了。【参考方案4】:

取决于您需要实现什么才能做好。插入顺序通常不重要,因此无需维护它,您可以重新排列以获得更好的性能。

对于 Map,通常使用 HashMap 和 TreeMap。通过使用哈希码,可以将条目分成易于搜索的小组。TreeMap 以较慢的搜索速度为代价维护插入条目的排序顺序,但比 HashMap 更易于排序。

【讨论】:

【参考方案5】:

当您使用 HashSet(或 HashMap)时,数据会根据对象的哈希值存储在“桶”中。通过这种方式,您的数据更易于访问,因为您不必在整个 Set 中查找这些特定数据,您只需在正确的存储桶中查找即可。

这样你可以提高特定点的表现。

每个 Collection 实现都有其特殊性,以使其在特定条件下更好地使用。这些特性中的每一个都是有代价的。因此,如果您真的不需要它(例如插入订单),您最好使用不提供它但更适合您的要求的实现。

【讨论】:

【参考方案6】:

为什么要保持插入顺序?如果您使用HashMap,则可以通过key 获取条目。这并不意味着它不提供满足您需求的类。

【讨论】:

【参考方案7】:

O'Reilly Java Cookbook 中有一节叫做“避免排序的冲动”。您应该问的问题实际上与您最初的问题相反......“我们通过排序获得了一些东西吗?”排序和维护该顺序需要付出很多努力。当然排序很容易,但在大多数程序中通常无法扩展。如果您要每秒处理数千或数万个请求(insrt、del、get 等),那么无论您使用的是排序的还是未排序的数据结构都非常重要。

【讨论】:

【参考方案8】:

好的......所以这些帖子与现在相比已经过时了,但是根据您的需要或应用程序要求需要插入顺序,所以只需使用正确的集合类型。在大多数情况下,它不是必需的,但是在您需要按照对象的存储顺序使用对象的情况下,我认为这是一个明确的需求。我认为,当您创建例如向导或流引擎或您需要从一个状态到另一个状态的类似性质的东西时,顺序很重要。从这个意义上说,您可以从列表中读取内容,而无需跟踪您接下来需要什么或遍历列表以找到您想要的内容。从这个意义上说,它确实有助于提高性能。这确实很重要,否则这些集合就没有多大意义了。

【讨论】:

【参考方案9】:

一些Collection没有维护顺序,因为它们计算内容的hashCode并将其相应地存储在适当的桶中。

【讨论】:

【参考方案10】:

我无法引用参考,但通过设计,Collection 接口的ListSet 实现基本上是可扩展的Arrays。正如Collections 默认提供的在任何时候动态addremove 元素的方法——Arrays 没有——插入顺序可能不会被保留。 因此,随着内容操作的方法越来越多,需要特殊的实现来保持顺序。

另一点是性能,因为性能最好的Collection 可能不是那个,它保留了它的插入顺序。但是我不确定Collections 究竟如何管理他们的内容以提高性能。

所以,简而言之,我能想到为什么会有保留顺序的Collection 实现的两个主要原因是:

    类架构 性能

【讨论】:

请注意,Arrays 是一个实际的类,而数组是一种特殊类型的容器对象。我也很确定LinkedList 实际上确实使用了链表,但我还没有阅读代码。 :-) 好的,点了,我编辑了我的帖子。关于您的LinkedList 评论:与我发布的内容矛盾在哪里? 澄清一下:LinkedList afaik 是一个List(读取可扩展Array),其插入顺序在另一个List 中维护(这两个是链接的 i>,因此得名)。或者,我错了吗? 一个非常混乱和令人困惑的帖子。 LinkedList 不是可扩展数组,List 也不是:它取决于实现。它们都不包含“另一个列表”。我不知道您所说的“哪些数组没有”是什么意思。你的第二段基本上没有意义。你的结论不是从你的前提中得出的。 另外,Array 对象不提供动态删除和添加元素的方法。这就是为什么List 首先存在的原因。我的第二段说明了您在帖子中所说的内容。哥们,不要对你的第一印象大发雷霆。

以上是关于维护插入顺序的 Java 集合的主要内容,如果未能解决你的问题,请参考以下文章

java集合

Java 集合框架:LinkedHashMap

Java_集合框架SetHashSetLinkedHashSetTreeSet使用区别

JAVA集合LinkedHashMap及其源码分析

Set无序怎么办?

像frozenset这样维护插入顺序的数据结构?