【问题标题】:Any collection O(1) indexOf time complexity?任何集合 O(1) indexOf 时间复杂度?
【发布时间】:2020-10-11 12:21:40
【问题描述】:

谁能帮我弄清楚,Java 中是否有任何集合类型(或任何语言竞争性的集合类型)为 indexOf(Item item) 操作提供 O(1) 时间复杂度? [你可以假设没有重复或只是返回的第一个索引]

如果什么都没有,谁能帮我推断一下为什么没有形成这样的原因?有什么特别的原因吗?

【问题讨论】:

  • @tgdavies 感谢您的链接,但我特别询问具有该特征的集合。我知道,我可以使用地图作为解决方法。 [虽然不完美]
  • 为什么你认为[虽然不完美]?想想如果对象不是唯一的,你将如何找到索引?
  • @Eklavya 我正在寻找一个类似数据结构的集合。在我的特定情况下,元素是唯一的(不重复)
  • 我能想到的唯一方法是列表索引是否可以从元素的属性中派生。例如,如果list[0,1,2,3,4,5],您可以找到list.indexOf(3)。但一般情况下,您不能,因为您需要在列表中进行搜索。

标签: java collections big-o indexof


【解决方案1】:

没有具有该属性的 Java(标准)集合类型。

indexOf(Object) 方法在List 中声明,并且没有任何List 实现提供比O(N) 更好的查找。

还有其他集合类型提供O(logN)O(1) 查找,但它们不会像indexOf(Object) 那样返回位置(和索引)。 (HashMapHashSet 分别提供 get(key)contains(object)O(1)。)


如果没有,任何人都可以帮我推断出没有形成这种情况的原因吗?有什么特别的原因吗?

因为确实提供此类功能的数据结构会很复杂,并且(可能)更慢和/或结合ArrayListHashMap的缺点。

我想不出一个好的方法来实现快速查找的列表;即没有严重缺点的一种。如果有一个好的方法,我希望 Java 团队知道它......并将它包含在 Java SE 库中。这类似于为什么没有通用的并发列表类型。

我并不是说这是不可能的。我是说,如果可能的话,没有人能找到一种方法来做到这一点。 AFAIK。

设计通用集合类型涉及妥协。当您针对一种访问模式进行优化时,您通常会针对另一种访问模式进行反优化。

【讨论】:

    【解决方案2】:

    要在O(1) 的集合中查找元素的索引,您需要能够直接 检索元素的索引而无需搜索。 Listarray 不允许我们这样做(需要 O(N))并且 Set 未排序 1

    我们留下了Map,但 Map 是一个 键值对

    您可以创建一个映射,其中键作为元素,值作为索引,作为集合的辅助数据结构。缺点是更新集合(列表/数组)时需要更新地图

    或者只是使用地图并摆脱您的收藏。


    1indexOf 方法位于 List 而不是 Collection

    【讨论】:

      【解决方案3】:

      Map 似乎是最简单的解决方案,但只是为了好玩:(实现 List 的其余部分作为练习留给读者)

      package org.example;
      
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      import java.util.Set;
      import java.util.TreeSet;
      
      public class HashList<E> {
          private final List<E> list = new ArrayList<>();
          private final Map<E,Set<Long>> indexes = new HashMap<>();
      
          public long indexOf(E item) {
              Set<Long> indexSet = indexes.get(item);
              if (indexSet == null || indexSet.isEmpty()) {
                  return -1;
              } else {
                  return indexSet.iterator().next();
              }
          }
      
          public void add(E item) {
              list.add(item);
              long index = list.size() - 1;
              Set<Long> indexSet = indexes.get(item);
              if (indexSet == null) {
                  indexSet = new TreeSet<>();
                  indexes.put(item, indexSet);
              }
              indexSet.add(index);
          }
      
          public static void main(String... args) {
              HashList<String> list = new HashList<>();
      
              assertTrue(list.indexOf("foo") == -1);
              list.add("foo");
              assertTrue(list.indexOf("foo") == 0);
              list.add("bar");
              assertTrue(list.indexOf("bar") == 1);
              list.add("foo");
              // still 0 for the first occurrence
              assertTrue(list.indexOf("foo") == 0);
          }
      
          private static void assertTrue(boolean truth) {
              if (!truth) {
                  throw new RuntimeException();
              }
          }
      }
      

      【讨论】:

      • 相当昂贵。当索引永远不能超过int 的值范围时,为什么还要使用Long
      猜你喜欢
      • 1970-01-01
      • 2015-05-25
      • 1970-01-01
      • 1970-01-01
      • 2016-04-21
      • 2019-11-09
      • 1970-01-01
      • 2017-11-13
      • 1970-01-01
      相关资源
      最近更新 更多