【问题标题】:List with fast contains快速包含的列表
【发布时间】:2011-12-27 08:08:06
【问题描述】:

我想知道是否有一个List 实现允许快速contains。我正在使用很长的List,但我无法切换到Set,因为我需要按索引访问。我可以忽略性能,它现在可能可以接受,将来可能可以接受,也可能不可以接受。我可以创建一个HashSet 并对两者进行所有修改操作,但是手动操作非常无聊且容易出错。

我知道不可能有一个像 List 和 Set 一样工作的类(因为 equals 语义不同),但我想知道是否有 List 实现 RandomAccess 并使用 @987654331 @ 用于加速 contains.

【问题讨论】:

  • 哈希列表应该很快,但它不是标准 JAVA API 的一部分
  • 你能保持你的列表排序并使用二进制搜索包含吗?
  • 他需要通过索引进行随机访问,如果插入时索引发生变化,这是不可能的。

标签: java list set


【解决方案1】:

我知道不可能有一个像 List 和 Set 一样工作的类

你试过LinkedHashSet吗?从技术上讲,它是一个集合,但它保留了可能对您来说足够的顺序。但是按索引访问是线性的,不是内置的。

其他方法是使用自定义 装饰器 包装 List,这两个装饰器都委托给 List 并维护一个内部Set 以更快地contains。

【讨论】:

  • +1 或 LinkedHashMap -- 如果内存不是问题,两者都很好
  • 他指定他需要List 接口来按索引访问(不过,除非他使用 ArrayList,否则它不会是 O(1))
【解决方案2】:

你可以包装一个列表和 hashSet 结合两全其美

public class FastContainsList<T> extends AbstractSequentialList<T> implements RandomAccess{
//extending sequential because it bases itself of the ListIterator(int) and size() implementation

    private List<T> list=new ArrayList<T>();
    private Set<T> set=new HashSet<T>();


    public int size(){
        return list.size();
    }

    public boolean contains(Object o){//what it's about
        return set.contains(o);
    }

    public ListIterator<T> listIterator(int i){
        return new ConIterator(list.listIterator(i));
    }

    /*for iterator()*/
    private ConIterator implements ListIterator<T>{

        T obj;
        ListIterator<T> it;

        private ConIterator(ListIterator<T> it){
        this.it = it
        }

        public T next(){
        return obj=it.next();
        }

        public T previous(){
        return obj=it.previous();
        }

        public void remove(){
        it.remove();//remove from both
        set.remove(obj);
        }

        public void set(T t){
            it.set(t);
            set.remove(obj);
            set.add(obj=t);
        }

         public void add(T t){
             it.add(t);
             set.add(t);
         }

        //hasNext and hasPrevious + indexes still to be forwarded to it
    }

}

【讨论】:

  • 我实际上是在寻找一个“预制解决方案”,一个提供经过良好测试的类的库。但它似乎不存在,所以我接受你的回答。
  • 如果您的FastContainsList 包含两次相同的元素并且您在其上使用remove(.),则此实现将产生问题。当您的内部列表仍然存在时,您的内部集将不再包含该元素。例如。 fcl.add("a"); fcl.add("a"); fcl.remove("a"); 现在fcl 的内部列表仍然包含“a”,而集合没有。即,fcl.contains("a"); 将错误地返回 false。
  • @dotwin 如果您需要允许双打,那么您将 HashSet 更改为 HashMap&lt;T, Integer&gt; 并跟踪 T 在列表中出现的次数以及是否删除了从地图中删除的最后一个.
  • 我同意@dotwin,但它是一种可以在适当情况下使用的解决方案。我想可以添加一个检查以确保列表和集合是同步的。它只需要确保 set.size() 和 list.size() 在添加/删除操作之后是相同的。
  • @mrswadge 以最少的努力,您可以禁止重复或跟踪列表中每个项目的数量(您可以使用 Map&lt;T, Integer&gt; 来完成)
【解决方案3】:

BiMap&lt;Integer, MyClass&gt; 怎么样?这可以在Guava找到。

BiMap<Integer, MyClass> map = HashBiMap.create();

//store by index
map.put(1, myObj1);

//get by index
MyClass retrievedObj = map.get(1);

//check if in map
if ( map.containsValue(retrievedObj) ) {
   //...
}

我知道它没有实现List 接口。这里的主要限制是不提供传统的List 意义上的插入和删除;但你没有具体说明这些对你是否重要。

【讨论】:

    【解决方案4】:

    【讨论】:

    • 鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。
    • 应该,但也许没有。该文档没有说明任何关于 contains 的内容。
    • 查看源代码和一些单独的测试表明,TreeList 上的contains 在最坏的情况下(元素不在列表中)充其量是线性时间操作。我还没有彻底分析它以弄清楚它实际上是什么,但它可能比这更糟。在我的测试中,ArrayList 和 LinkedList 对于 contains 方法的性能明显更快。
    【解决方案5】:

    您可以维护一个列表和集合。这将为您提供快速索引查找并包含(开销很小)

    顺便说一句:如果您的列表很小,例如10 个条目,使用普通列表可能没有任何区别。

    【讨论】:

    • 我总是对“如果你的名单很小”这件事很感兴趣。到目前为止,在我认为这是一个小列表的所有情况下,将来有人会在其中放入 1000 万个元素。
    • @trejkaz 有收藏,例如如果你有大量的听众,你做错了什么。将侦听器存储在一个集合中会使正常用例变得昂贵,并且在开发人员做错事的情况下,您可能只是隐藏了他们做错事的事实。有时规定性是有意义的。
    【解决方案6】:

    我认为包含HashMap 和List 的类(正如您在帖子中所说)可能是快速contains 和access 的最佳选择。

    【讨论】:

      猜你喜欢
      • 2014-09-04
      • 2012-05-24
      • 1970-01-01
      • 2012-06-16
      • 1970-01-01
      • 2013-11-26
      • 1970-01-01
      • 2021-12-26
      • 2011-05-10
      相关资源
      最近更新 更多