중복되지 않는 목록 구현이 있습니까?


87

나는 알고 SortedSet있지만, 내 경우에는 그 구현 뭔가가 필요 List, 그리고 Set. 그렇다면 API 또는 다른 곳에 구현이 있습니까?

나 자신을 구현하는 것은 어렵지 않지만, 여기 사람들에게 먼저 물어 보는 것은 어떨까?


1
List를 구현해야하는 이유는 무엇입니까? 세트는 목록처럼 반복 가능하므로 수신 방법이 다른 이유로 List를 시행한다고 가정합니다.
Rob

@Rob 맞습니다. 외부 요구 사항이며 데이터 구조에는 하나 이상의 목록이 포함되어 있습니다.
Yuval

사용자가 LIST를 원하면 SET 인터페이스에없는 LIST 인터페이스의 메서드가 필요하다는 것이 분명합니다.
marcolopes

답변:


94

이 작업을 수행하는 표준 라이브러리에는 Java 컬렉션이 없습니다. LinkedHashSet<E>List그래도 a와 유사하게 순서를 유지 하므로 a List로 사용하고 싶을 때 세트를 a로 래핑 하면 원하는 List의미를 얻을 수 있습니다.

또는 Commons Collections (또는 commons-collections4일반 버전의 경우)에는 List이미 원하는 작업을 수행하는 SetUniqueList/ SetUniqueList<E>.


6
Commons 클래스는 정확히 내가 필요로하는 것이지만, 제 상사가 결국 직접 구현하라고했습니다. 어쨌든 10 배!
Yuval

5
아 글쎄, 바퀴를 재창조하는 것과 같은 것은 없습니다! 어쨌든 필요가 다시 생기면 이제 알게 될 것입니다. collections15는 매우 유용한 기능입니다. 특히 MultiMaps는 자신을 많이 구현하는 일의 고통을 덜어줍니다.
Calum

19
@skaffman : 그는 실제로 바보는 아니지만 때때로 그는 ... 글쎄, 이상합니다. 어쨌든 나는 제품에 버그를 도입하지 않을 것입니다. 오늘날의 시장에서 나는 내 일에 만족하고 내 요점을 이해한다면 문을 닫고 다리를 태우는 것을 원하지 않습니다.
Yuval

3
SetUniqueList에 매개 변수화 된 유형이 없을 때 매우 놀랍습니다.
emeraldhieu

2
Jeffrey : 모바일 플랫폼에서 시스템은 일반적으로 사용되지 않는 클래스를 제거하지만 이러한 "정상적인"솔루션 중 하나를 사용하지 않을 수있는 많은 이유가 있습니다. 항상 약간의 절충안이 있으며 모든 경우를 해결하는 해결책은 없습니다.
Calum

14

여기 내가 한 일이 있으며 작동합니다.

내가이 있으리라 믿고 ArrayList내가 한 첫 번째 일은 함께 작동하도록하는 것은 새를 만들었습니다 LinkedHashMap.

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

그런 다음 새 요소를 LinkedHashSet. add 메서드는를 변경하지 않고 LinkedHasSet새 요소가 중복 된 경우 false를 반환합니다. 그래서 이것은 ArrayList.

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

이것은 중복 항목이 배열 목록에 추가되는 것을 방지하는 간단하고 우아한 방법입니다. 원하는 경우 .NET Framework를 확장하는 클래스에서이를 캡슐화하고 add 메서드를 재정의 할 수 있습니다 ArrayList. addAll요소를 반복하고 add 메서드를 호출하여 처리하는 것을 잊지 마십시오.


1
예, 이것이 최선의 해결책이라고 생각합니다. Linked가 아닌 일반 HashSet을 사용할 수도 있습니다. 그런 다음 원하는대로 목록을 사용할 수 있습니다. 특정 색인 앞에 목록 안에 요소를 추가하면 복제 된 항목을이 위치로 이동 하려는지 여부를 결정할 수 있습니다.
gyurix

가장 좋은 방법은 여기에 ... 내 UniqueList 클래스 코드 게시 할 예정입니다
marcolopes

이것은 내 BFS 그래프 알고리즘에서 나를 위해 일했습니다. 나는 그들이 이미되지 않은 단지 만약 내가 대기열 (LinkedList의)에 추가 한 일부 노드가 있었기 때문에.
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 ()는 전체 목록을 스캔하여 개체가 목록에 포함되어 있는지 확인해야합니다. 즉, 큰 목록에 개체를 추가 할 때 각 추가 작업에 대해 전체 목록이 검색됩니다 (최악의 경우). 이것은 느려질 수 있습니다.
matt b

8
또한 addAll 재정의는 addAll ()에 전달되는 컬렉션의 중복을 확인하지 않습니다.
matt b

@mattb 그렇다면이 문제를 어떻게 해결 하시겠습니까? Android에서 개체를 목록 항목보기에 바인딩 할 때보기 어댑터에서 항목의 위치가 제공됩니다. 세트에는 인덱스가 없으므로 목록을 사용할 때 객체가 존재하는지 여부를 확인하는 유일한 방법은 반복하여 기존 사본을 찾는 것입니다.
TheRealChx101

6

목록으로 집합을 캡슐화하지 않는 이유는 다음과 같습니다.

new ArrayList( new LinkedHashSet() )

이것은 Collections의 진정한 마스터 인 누군가를 위해 다른 구현을 남겨 둡니다 ;-)


4
이 생성자는 Set의 내용을 래핑하는 대신 새 List에 복사합니다.
Calum

@Calum, 맞습니다.하지만 List에 중복 항목을 추가하지 않을까 걱정하는 대신에 그는 자신의 개체를 Set에 추가하고 (Set가 중복 항목을 필터링하는 것에 대해 걱정하게 할 수 있습니다) 전달할 때 해당 Set을 List로 래핑 할 수 있습니다. 외부 방법.
matt b

4
이것은 목록에 세트를 복사하지만 잘 알려진 순서가 없습니다. 그러나 이것이 질문의 전부입니다.
Janning

4

dhiller의 대답을 진지하게 고려해야합니다.

  1. 중복이없는 목록에 개체를 추가하는 것에 대해 걱정하는 대신, 기본적으로 중복 항목을 필터링하는 집합 (모든 구현)에 추가합니다.
  2. List가 필요한 메서드를 호출해야하는 경우이를 a new ArrayList(set)(또는 a new LinkedList(set)등)로 래핑합니다 .

귀하가 게시 한 솔루션 NoDuplicatesList에는 주로 contains()메서드에 문제가 있으며 클래스가 addAll()메서드에 전달 된 Collection의 중복 검사를 처리하지 않는다고 생각합니다 .


이 contains () 문제에 대해 배우고 싶습니다. addAll ()에 관해서는 주어진 컬렉션의 복사본을 만들고 이미 'this'에있는 모든 개체를 제거합니다. 중복을 어떻게 처리하지 않습니까?
Yuval

클래스 게시에 대한 내 의견에서 언급했듯이 contains ()는 전체 목록 (최악의 경우)을 스캔하여 개체가 목록에 포함되어 있는지 확인해야합니다. 백만 개의 항목 목록이 있고 여기에 개별적으로 10 개를 추가하면 (최악의 경우) 천만 개 이상의 항목이 스캔됩니다.
Matt b

addAll ()의 경우 addAll에 전달 된 Collection에 중복 된 내용이 포함되어 있으면 감지되지 않습니다. 예 : 목록 {A, B, C, D} 매개 변수 목록 {B, D, E, E, E}. 매개 변수의 사본을 작성하고 removeAll 후에 {E, E, E}를 포함합니다.
matt b

절차 전반에 걸쳐 NoDuplicatesList를 사용하고 addAll ()이 다른 NoDuplicatesList를 매개 변수로 받아야하므로 addAll () 문제는 나에게 실제로 관련이 없습니다. contains () 성능을 향상시키기 위해 무엇을 제안 하시겠습니까?
Yuval

3

나는 갔다, 그래서 나는 그런 일을 필요 평민 컬렉션 과를 사용 SetUniqueList하지만 일부 성능 테스트를 실행했을 때, 나는 그것이 내가 사용하려는 경우 경우에 비교 최적화되지 않은 것 같습니다 것을 발견 Set하고 얻을 Array사용 Set.toArray()방법을.

다른 구현에 비해 100,000 개의 문자열 을 채운 다음 순회 하는 SetUniqueTest20 : 1의 시간 이 걸렸 는데 이는 큰 차이입니다.

따라서 성능이 걱정된다면를 사용하는 대신 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


1

참고 : 하위 목록 구현은 고려 하지 않습니다 .

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

그 이유는 (컬렉션 대신) 목록을 매개 변수로 사용하는 API와 상호 작용하기 때문입니까? 처리해야하는 약간 성가신
matt b

실제로 API는 Map <AccountType, Map <AccountType, List <Account >>>를 사용합니다. 즉, 수십에서 수백 개의 목록을 보유하고 있다는 의미입니다.
Yuval

요소-확률 쌍을 사용하여 확률 함수를 생성하는 것은 중복 요소를 병합 할 수 있지만 중복을 포함하지 않을 수 있습니다.
Al G Johnston

-1

add방법, 왜 사용하지 않는 HashSet.add()대신 중복을 확인 HashSet.consist(). 중복이 없으면 HashSet.add()반환 true됩니다 false.


무엇입니까 HashSet#consist()?
naXa

-1

내 머리 꼭대기에서 목록은 중복을 허용합니다. 상속 된 메서드를 호출하기 전에를 빠르게 구현하고 UniqueArrayList모든 add/ insert함수를 재정 의하여 확인할 contains()수 있습니다. 개인적인 용도로 사용하는 경우에만 add사용 하는 방법을 구현하고 미래의 프로그래머가 다른 방식으로 목록을 사용하려고 할 경우 예외를 throw하도록 다른 방법을 재정의 할 수 있습니다.


아무도 더 나은 것을 제안하지 않으면이 아이디어로 돌아갈 준비가되었습니다. = 8-) 위의 내 대답을 참조하십시오.
Yuval

-3

나는 다음과 같이 내 자신의 작은 라이브러리에서 내 고유 목록을 만들었습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.