有没有重复的列表实现?


86

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

实施我自己应该不难,但是我想到了为什么不先问这里的人呢?


1
为什么需要实施List?集是可迭代的,就像列表一样,因此我认为接收方法由于其他一些原因正在强制执行List。
罗布

@Rob是的,这是一个外部需求,数据结构包含的地狱远远不止一个List。
Yuval,

如果用户想要一个LIST,那么很显然需要LIST接口的方法在SET接口中不存在...
marcolopes

Answers:


92

标准库中没有Java集合可以做到这一点。不过,LinkedHashSet<E>保留顺序类似于List,因此如果要将集合包装为时List要使用List您将获得所需的语义。

另外,Commons Collections(或commons-collections4对于通用版本)也具有List您想要的功能:SetUniqueList/ SetUniqueList<E>


5
下课正是我所需要的,但是老板告诉我最终要自己实施。还是10倍!
Yuval

5
嗯,没什么比重新发明轮子了!无论如何,您现在将知道是否再次出现需求。收集是一件非常有用的事情;特别是MultiMaps减轻了人们最终实现自己的痛苦。
Calum

19
@skaffman:他实际上不是一个白痴,但有时他的举动是……好,很奇怪。无论如何,我不会在产品中引入错误。在当今的市场中,我对自己的工作感到满意,并且如果您明白我的意思,也不想敲门和烧桥梁。
Yuval

3
当SetUniqueList没有参数化类型时,我感到很惊讶。
emeraldhieu 2012年

2
杰弗里(Jeffrey):在移动平台上,系统通常会删除未使用的类,但是可以肯定,由于很多原因,您可能不赞成使用这些“常规”解决方案之一。总是需要权衡取舍,没有解决方案可以解决所有问题。
Calum 2013年

14

这是我所做的,并且有效。

假设我ArrayList要做的第一件事是创建一个新的LinkedHashMap

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

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

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

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


1
是的,我认为,这是最好的解决方案,您也可以只使用普通的HashSet而不是Linked,然后可以根据需要使用列表,也可以忽略某些情况下的操作,例如在列表中的特定索引之前添加元素,您可以不希望将重复项移至该位置。
gyurix 2015年

最好的解决方案……将发布我的
UniqueList

在我的BFS Graph算法中,这对我有用。因为我有一些节点,我添加到队列(链表)只是如果他们不是已经英寸
Jeancarlo Fontalvo

11

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

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);
        }
    }
}   

10
注意-LinkedList.contains()需要扫描整个列表,以确定对象是否包含在List中。这意味着在将对象添加到大型列表时,将扫描整个列表以进行每个添加操作(在最坏的情况下)。最终可能会变慢。
马特b

8
另外,您的addAll重写不会检查传递给addAll()的集合中的重复项。
马特b

@mattb那么您将如何解决此问题:在Android上,将对象绑定到列表项视图时,将获得该项在视图适配器中的位置。由于集合没有索引,因此唯一的方法是在使用列表时检查对象是否存在,即遍历并查找现有副本。
TheRealChx101 '19

6

为什么不用列表封装一个集合,就像这样:

new ArrayList( new LinkedHashSet() )

这为真正的Collections大师留下了另一个实现;-)


4
此构造函数将Set的内容复制到新List中,而不是将其包装。
Calum

@Calum是正确的,但是他不必担心不将重复项添加到列表中,而是可以将其对象添加到Set中(让Set担心过滤掉重复项),并在将其传递给List时将其包装在List中外部方法。
马特b

4
这会将集合复制到列表,但是您没有任何已知的排序。但这就是问题的全部。
2012年

4

您应该认真考虑dhiller的答案:

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

我认为您随发布的解决方案NoDuplicatesList存在一些问题,大部分与contains()方法有关,再加上您的类无法处理传递给您的addAll()方法的Collection中是否有重复项的检查。


我很想学习这些contains()问题。至于addAll(),我创建给定集合的副本,并删除“ this”中已有的所有对象。那怎么不处理重复呢?
Yuval

正如我在对类发布的评论中提到的那样,contains()必须扫描整个列表(在最坏的情况下)以查找对象是否包含在列表中。如果您拥有一百万个项目的列表,并单独添加十个项目,那么(在最坏的情况下)将扫描超过一千万个项目。
马特b

至于addAll(),如果传递给addAll的Collection本身包含重复项,则不会检测到重复项。例如:您的列表{A,B,C,D}参数列表{B,D,E,E,E}。创建该参数的副本,在removeAll之后,它包含{E,E,E}。
马特b

因为我在整个过程中都使用NoDuplicatesList,所以addAll()问题与我并不真正相关,并且addAll()应该接收另一个NoDuplicatesList作为其参数。您对提高contains()性能有何建议?
Yuval

3

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

SetUniqueTest花了20:1的时间填写,然后遍历100000个弦乐相对于其他实现,这是一个大问题不同。

因此,如果您担心性能,建议您使用Set and Get 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


1

注意:它不考虑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));
    }

}

0

收集接口文档说:

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

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


我特别提到我需要一个List实现。相信我,这是有原因的。
Yuval,

原因是因为您正在与将List作为参数(而不是Collection)的API进行交互?必须处理的内容有点令人讨厌
matt b

实际上,API接受Map <AccountType,Map <AccountType,List <Account >>>,这意味着保存在数十到数百个列表的附近。
Yuval

尽管元素可以直接合并,但使用元素-概率对构造概率函数可能不会包含重复项。
Al G Johnston

-1

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


什么HashSet#consist()
naXa

-1

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


如果没有人提出更好的建议,我准备回到这个想法(最终我不得不这样做)= 8-)参见上面我的回答。
Yuval,

-3

我只是在自己的小库中创建了自己的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接口。
gyurix 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.