【问题标题】:java constantly sorted list with quick retrievaljava不断排序列表,快速检索
【发布时间】:2026-01-27 12:10:01
【问题描述】:

我正在寻找一个在java中不断排序的列表,它也可以用来非常快速地检索一个对象。 PriorityQueue 非常适合“不断排序”的要求,而 HashMap 非常适合按键快速检索,但我需要将两者放在同一个列表中。有一次我自己写了,但它没有实现集合接口(所以不能用作 java.util.List 等的替代品),我宁愿坚持使用标准的 java 类如果可能的话。

那里有这样的清单吗?现在我正在使用 2 个列表,一个优先级队列和一个哈希图,它们都包含相同的对象。我使用优先级队列按排序顺序遍历列表的第一部分,通过键快速检索的哈希图(我需要交替执行这两个操作),但我希望有一个更优雅的解决方案......

编辑:我应该补充一点,我需要让列表按不同的比较器排序,然后是用于按键检索的列表;列表按long值排序,检索键是String。

【问题讨论】:

    标签: java sortedlist


    【解决方案1】:

    由于您已经在使用HashMap,这意味着您拥有唯一的密钥。假设您想通过这些键订购,TreeMap 就是您的答案。

    【讨论】:

    • 感谢您的回复...请查看我几秒钟前的编辑,我认为 TreeMap 不会在这里工作。
    • @user85116 - 不,不会。但是您的问题没有提供足够的信息来给出一个好的答案。例如,您说您当前正在使用 PriorityQueue。这是否意味着您只关心“列表”中的第一项是否已排序?或者你想要一个总排序(在这种情况下 PriorityQueue 是错误的选择)。如果您使用实际用例编辑您的问题,那么我将编辑答案。
    • 我不明白为什么 PriorityQueue 是错误的选择;正如我的问题中提到的,有时我需要按排序顺序遍历列表的第一部分(不是整个列表,只是开头的几个元素)(PriorityQueue 做得很好);我的问题是,在其他时候,我需要根据一个键从列表中抓取一个项目,并且这种查找需要快速。
    • @user85116 - 你的问题要求不同的东西。 “不断排序”意味着对整个列表进行排序,而 PriorityQueue 没有这样做。就此而言,“列表”意味着您对 Map 的使用与某些行为相矛盾。正如我上面所说,如果你描述你的确切用例,而不是你当前的解决方案,人们可​​能会给你更好的建议。如果你不想这样做,那我只能说“祝你好运”。
    • PriorityQueue 完全符合要求;我可以遍历列表的第一部分,并且知道我得到的每个项目都是按排序顺序排列的。如果我将另一个项目添加到队列中,我可以重新遍历前几个项目,因为我知道我的新项目将以正确的顺序到达。这是事实,对不起。我同意我对“列表”一词的使用有时有点模糊。
    【解决方案2】:

    听起来你说的是一个带有自动维护索引的集合。

    尝试查看GlazedLists,它使用“列表管道”来有效地传播更改——他们的SortedList 类应该可以完成这项工作。

    编辑:错过了您的按键检索要求。这可以通过GlazedLists.syncEventListToMapGlazedLists.syncEventListToMultimap 来实现——如果没有重复键,则syncEventListToMap 有效,如果有重复键,则syncEventListToMultimap 有效。这种方法的好处在于您可以根据不同的索引创建多个地图。


    如果您想将 TreeMaps 用于索引 - 这可能会给您带来更好的性能 - 您需要将 TreeMaps 私有封装在您选择的自定义类中,该类公开您想要的接口/方法,并创建访问器/该类的变异器以使索引与集合保持同步。如果您从多个线程访问集合,请务必处理并发问题(通过 synchronized 方法或锁等)。


    编辑:最后,如果按排序顺序快速遍历项目很重要,请考虑使用ConcurrentSkipListMap 而不是 TreeMap——不是因为它的并发性,而是因为它的快速遍历。 Skip lists 是具有多级链接的链表,一个遍历所有项目,下一个平均遍历每 K 个项目(对于给定的常数 K),下一个遍历每个 K2 个项目平均等。

    【讨论】:

      【解决方案3】:
      【解决方案4】:

      使用TreeSet

      基于 TreeMap 的 NavigableSet 实现。元素使用它们的自然顺序或在集合创建时提供的 Comparator 进行排序,具体取决于使用的构造函数。

      此实现为基本操作(添加、删除和包含)提供有保证的 log(n) 时间成本。

      【讨论】:

        【解决方案5】:

        我没有对此进行测试,所以我可能是错的,所以认为这只是一次尝试。 使用 TreeMap,将此映射的键包装为具有两个属性的对象(您在 hashmap 中用作键的字符串和在 PriorityQueue 中用于维护排序顺序的 long)。现在对于这个对象,使用字符串覆盖 equals 和 hashcode 方法。使用 long 实现可比较的接口。

        【讨论】:

          【解决方案6】:

          为什么不将解决方案封装到实现 Collection 或 Map 的类中?
          这样,您可以简单地将检索方法委托给更快/更好的集合。只需确保调用 write-methods (add/remove/put) 将被转发到两个集合。记住间接访问,例如 iterator.remove()。大多数这些方法都是可选的,但您必须停用它们(Collections.unmodifiableXXX 在大多数情况下会有所帮助)。

          【讨论】: