【问题标题】:Is there a no-duplicate List implementation out there?那里有没有重复的 List 实现吗?
【发布时间】:2010-09-21 02:03:55
【问题描述】:

我知道SortedSet,但就我而言,我需要实现List 的东西,而不是Set。那么在 API 或其他地方是否有实现?

自己实现应该不难,但我想为什么不先在这里问人呢?

【问题讨论】:

  • 为什么需要实现List?集合是可迭代的,就像列表一样,所以我想接收方法是出于其他原因强制执行列表。
  • @Rob 没错,这是一个外部需求,而且数据结构包含的不止一个List。
  • 如果用户想要一个 LIST,那么显然需要 LIST 接口的方法,而 SET 接口不存在...

标签: java list collections duplicates


【解决方案1】:

标准库中没有用于执行此操作的 Java 集合。 LinkedHashSet<E> 保留了与 List 类似的顺序,因此,如果您将集合包装在 List 中,当您想将其用作 List 时,您将获得所需的语义。

或者,Commons Collections(或commons-collections4,对于通用版本)有一个List,它可以满足您的需求:SetUniqueList/SetUniqueList<E>

【讨论】:

  • Commons 类正是我需要的,但我的老板告诉我最终要自己实现它。还是 10 倍!
  • 啊,没有什么比重新发明轮子更好的了!无论如何,你现在会知道是否需要再次出现。 collections15 是一个非常有用的东西; MultiMaps 尤其减轻了一个人最终实现自己的痛苦。
  • @skaffman:他实际上并不是个白痴,但有时他会做出……嗯,奇怪的动作。无论如何,我不会在产品中引入错误。在今天的市场上,我对自己的工作很满意,如果你明白我的意思,我不想关门和烧桥。
  • 当 SetUniqueList 没有参数化类型时我很惊讶。
  • Jeffrey:在移动平台上,系统通常会删除未使用的类,但可以肯定的是,有很多原因您可能不会选择这些“正常”解决方案之一。总是需要做出一些权衡,没有解决方案可以解决所有情况。
【解决方案2】:

这就是我所做的,它确实有效。

假设我有一个ArrayList 可以使用,我做的第一件事就是创建一个新的LinkedHashMap

LinkedHashSet<E> hashSet = new LinkedHashSet<E>()

然后我尝试将我的新元素添加到LinkedHashSet。 add 方法不会改变 LinkedHasSet 并且如果新元素是重复的则返回 false。所以这成为我可以在添加到ArrayList 之前测试的条件。

if (hashSet.add(E)) arrayList.add(E);

这是防止将重复项添加到数组列表中的一种简单而优雅的方法。如果您愿意,可以将其封装在扩展 ArrayList 的类中并覆盖 add 方法。只要记住通过循环遍历元素并调用 add 方法来处理addAll

【讨论】:

  • 是的,我认为,这是最好的解决方案,你也可以简单地使用普通的HashSet,而不是Linked,然后你可以随意使用你的列表,你也可以决定什么在某些情况下,例如在特定索引之前在列表中添加元素时,您可以决定是否要将重复项移动到该位置。
  • 这里的最佳解决方案...将发布我的 UniqueList 类代码
  • 这对我有用,在我的 BFS Graph 算法中。因为我有一些节点添加到队列(LinkedList)中,只是它们还没有加入。
【解决方案3】:

这就是我最终所做的。我希望这对其他人有帮助。

class NoDuplicatesList<E> extends LinkedList<E> {
    @Override
    public boolean add(E e) {
        if (this.contains(e)) {
            return false;
        }
        else {
            return super.add(e);
        }
    }

    @Override
    public boolean addAll(Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(copy);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(index, copy);
    }

    @Override
    public void add(int index, E element) {
        if (this.contains(element)) {
            return;
        }
        else {
            super.add(index, element);
        }
    }
}   

【讨论】:

  • 小心 - LinkedList.contains() 需要扫描整个列表以确定对象是否包含在列表中。这意味着当您将对象添加到大型列表时,会扫描整个列表以查找每个添加操作(在最坏的情况下)。这最终可能会很慢。
  • 另外,您的 addAll 覆盖不会检查传递给 addAll() 的集合中的重复项。
  • @mattb 那么你将如何解决这个问题:在 Android 上,当将对象绑定到列表项视图时,我们会获得该项目在视图适配器中的位置。由于集合没有索引,所以在使用列表时检查对象是否存在的唯一方法是遍历并查找现有副本。
  • 这个解决方案的性能问题可以通过一个简单的附加Set&lt;Integer&gt; 来解决,该Set&lt;Integer&gt; 存储元素的哈希码(而不是搜索整个列表) -当然,这将需要所有元素正确实现 hashCode(),但是使用 Lombok 等辅助框架,这真的没问题……实际上,这有点微不足道。甚至可以使用用于 hashCodes 的红/黑树来优化 那个 解决方案......小的内存开销以获得大的性能提升;欢迎来到云计算的世界;-)
【解决方案4】:

为什么不用列表封装一个集合,排序如下:

new ArrayList( new LinkedHashSet() )

这将其他实现留给真正的收藏大师;-)

【讨论】:

  • 这个构造函数将 Set 的内容复制到新的 List 中,而不是包装它。
  • @Calum,这是正确的,但他不必担心不将重复项添加到 List 中,而是可以将他的对象添加到 Set 中(并让 Set 担心过滤掉重复项)然后将其包装起来将列表传递给外部方法时在列表中设置。
  • 这会将一个集合复制到一个列表中,但您没有任何众所周知的排序。但这就是问题所在。
【解决方案5】:

你应该认真考虑 dhiller 的回答:

  1. 不必担心将对象添加到无重复的列表中,而是将它们添加到 Set(任何实现)中,这样自然会过滤掉重复项。
  2. 当您需要调用需要 List 的方法时,请将其包装在 new ArrayList(set)(或 new LinkedList(set) 等)中。

我认为您使用NoDuplicatesList 发布的解决方案存在一些问题,主要是contains() 方法,而且您的类不处理传递给addAll() 方法的集合中的重复项检查。

【讨论】:

  • 我很想了解这些 contains() 问题。至于 addAll(),我创建给定集合的副本并删除“this”中已经存在的所有对象。那如何不处理重复项?
  • 正如我在对您的课程发布的评论中提到的, contains() 必须扫描整个列表(在最坏的情况下)以查找对象是否包含在列表中。如果您有一个包含 100 万个项目的列表并单独添加 10 个,那么(在最坏的情况下)会扫描超过 1000 万个项目。
  • 对于 addAll(),如果传递给 addAll 的 Collection 本身包含重复项,则不会检测到它们。例如:你的列表{A, B, C, D} 参数列表{B, D, E, E, E}。您创建参数的副本,并在 removeAll 之后包含 {E, E, E}。
  • addAll() 问题与我无关,因为我在整个过程中使用 NoDuplicatesList,并且 addAll() 应该接收另一个 NoDuplicatesList 作为其参数。你有什么建议来提高 contains() 的性能?
【解决方案6】:

我需要类似的东西,所以我去了 commons 集合 并使用了 SetUniqueList,但是当我进行一些性能测试时,我发现它似乎没有优化我想使用Set 并使用Set.toArray() 方法获得Array

与其他实现相比,SetUniqueTest 花了 20:1 时间 填充然后遍历 100,000 个字符串,这是一个很大的不同。

所以,如果你担心性能,我建议你使用Set and Get an Array而不是使用SetUniqueList,除非你真的需要SetUniqueList的逻辑,那么您需要检查其他解决方案...

测试代码主方法

public static void main(String[] args) {


SetUniqueList pq = SetUniqueList.decorate(new ArrayList());
Set s = new TreeSet();

long t1 = 0L;
long t2 = 0L;
String t;


t1 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    pq.add("a" + Math.random());
}
while (!pq.isEmpty()) {
    t = (String) pq.remove(0);
}
t1 = System.nanoTime() - t1;

t2 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    s.add("a" + Math.random());
}

s.clear();
String[] d = (String[]) s.toArray(new String[0]);
s.clear();
for (int i = 0; i < d.length; i++) {
    t = d[i];

}
t2 = System.nanoTime() - t2;

System.out.println((double)t1/1000/1000/1000); //seconds
System.out.println((double)t2/1000/1000/1000); //seconds
System.out.println(((double) t1) / t2);        //comparing results

}

问候, Mohammed Sleem

【讨论】:

    【解决方案7】:

    注意:它不考虑 subList 实现。

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Set;
    
    public class UniqueList<T> extends ArrayList<T> {
    
        private static final long serialVersionUID = 1L;
    
        /** Unique elements SET */
        private final Set<T> set=new HashSet();
    
        /** Used by addAll methods */
        private Collection<T> addUnique(Collection<? extends T> col) {
            Collection<T> unique=new ArrayList();
            for(T e: col){
                if (set.add(e)) unique.add(e);
            }
            return unique;
        }
    
        @Override
        public boolean add(T e) {
            return set.add(e) ? super.add(e) : false;
        }
    
        @Override
        public boolean addAll(Collection<? extends T> col) {
            return super.addAll(addUnique(col));
        }
    
        @Override
        public void add(int index, T e) {
            if (set.add(e)) super.add(index, e);
        }
    
        @Override
        public boolean addAll(int index, Collection<? extends T> col) {
            return super.addAll(index, addUnique(col));
        }
    
    }
    

    【讨论】:

      【解决方案8】:

      documentation for collection interfaces 说:

      Set — 一个不能包含重复元素的集合。
      List — 有序集合(有时称为序列)。列表可以包含重复的元素。

      因此,如果您不想重复,则可能不应该使用列表。

      【讨论】:

      • 我特别提到我需要一个 List 实现。相信我,这是有原因的。
      • 是因为您正在与一个将 List 作为参数(而不是 Collection)的 API 进行交互吗?处理起来有点烦人
      • 实际上 API 需要一个 Map>>,这意味着保存在几十到几百个列表附近的某个地方……呸。
      • 用元素-概率对构造概率函数可以不包含重复项,尽管可以合并重复的元素。
      【解决方案9】:

      这个呢? 只需检查列表,然后再添加包含现有对象的内容

      while (searchResult != null && searchResult.hasMore()) {
          SearchResult nextElement = searchResult.nextElement();
          Attributes attributes = nextElement.getAttributes();
      
          String stringName = getAttributeStringValue(attributes, SearchAttribute.*attributeName*);
         
         if(!List.contains(stringName)){
          List.add(stringName);
         }
      }
      

      【讨论】:

        【解决方案10】:

        add 方法中,为什么不使用HashSet.add() 而不是HashSet.consist() 来检查重复项。 如果没有重复,HashSet.add() 将返回 true,否则返回 false

        【讨论】:

        • HashSet#consist() 是什么?
        【解决方案11】:

        在我的脑海中,列表允许重复。在调用继承的方法之前,您可以快速实现 UniqueArrayList 并覆盖所有 add / insert 函数以检查 contains()。对于个人使用,您只能实现您使用的 add 方法,并覆盖其他方法以引发异常,以防将来的程序员尝试以不同的方式使用该列表。

        【讨论】:

        • 如果没有人提出更好的建议,我已经准备好回到这个想法(最终我不得不这样做)=8-) 请参阅上面我自己的答案。
        【解决方案12】:

        我刚刚在自己的小库中创建了自己的 UniqueList,如下所示:

        package com.bprog.collections;//my own little set of useful utilities and classes
        
        import java.util.HashSet;
        import java.util.ArrayList;
        import java.util.List;
        /**
        *
        * @author Jonathan
        */
        public class UniqueList {
        
        private HashSet masterSet = new HashSet();
        private ArrayList growableUniques;
        private Object[] returnable;
        
        public UniqueList() {
            growableUniques = new ArrayList();
        }
        
        public UniqueList(int size) {
            growableUniques = new ArrayList(size);
        }
        
        public void add(Object thing) {
            if (!masterSet.contains(thing)) {
                masterSet.add(thing);
                growableUniques.add(thing);
            }
        }
        
        /**
         * Casts to an ArrayList of unique values
         * @return 
         */
        public List getList(){
            return growableUniques;
        }
        
        public Object get(int index) {
            return growableUniques.get(index);
        }
        
        public Object[] toObjectArray() {
            int size = growableUniques.size();
            returnable = new Object[size];
            for (int i = 0; i < size; i++) {
                returnable[i] = growableUniques.get(i);
            }
            return returnable;
            }
        }
        

        我有一个如下所示的 TestCollections 类:

        package com.bprog.collections;
        import com.bprog.out.Out;
        /**
        *
        * @author Jonathan
        */
        public class TestCollections {
            public static void main(String[] args){
                UniqueList ul = new UniqueList();
                ul.add("Test");
                ul.add("Test");
                ul.add("Not a copy");
                ul.add("Test"); 
                //should only contain two things
                Object[] content = ul.toObjectArray();
                Out.pl("Array Content",content);
            }
        }
        

        工作正常。它所做的只是将它添加到一个集合中,如果它还没有它并且有一个可返回的 Arraylist 以及一个对象数组。

        【讨论】:

        • 是的,你应该添加更多的方法来实现 List 接口。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-06-23
        • 2010-09-18
        • 2010-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-15
        相关资源
        最近更新 更多