Java에서 고유 목록을 유지하는 방법은 무엇입니까?


104

Java에서 고유하거나 구별되는 개체 (중복 없음) 목록을 만드는 방법은 무엇입니까?

지금 HashMap<String, Integer>은 키를 덮어 써서 마지막에 HashMap.getKeySet()고유 한 것을 얻을 수 있기 때문에 이것을 사용 하고 있습니다. 하지만 여기에 가치 부분이 낭비되기 때문에 더 나은 방법이있을 것이라고 확신합니다.

답변:


164

Set 구현을 사용할 수 있습니다 .

JAVADoc의 일부 정보 :

중복 요소없는 컬렉션입니다 . 보다 공식적으로 집합에는 e1.equals (e2)와 같은 요소 e1 및 e2 쌍이없고 최대 하나의 null 요소가 포함됩니다. 이름에서 알 수 있듯이이 인터페이스는 수학적 집합 추상화를 모델링합니다.

참고 : 변경 가능한 개체가 집합 요소로 사용되는 경우에는 각별한주의가 필요합니다. 개체가 집합의 요소 인 동안 같음 비교에 영향을주는 방식으로 개체 값이 변경되면 집합의 동작이 지정되지 않습니다. 이 금지의 특별한 경우는 세트가 자신을 요소로 포함하는 것이 허용되지 않는다는 것입니다 .`

다음은 구현입니다.

  • HashSet

    이 클래스는 해시 함수가 버킷간에 요소를 적절하게 분산한다고 가정하여 기본 작업 (추가, 제거, 포함 및 크기 조정)에 대해 일정한 시간 성능을 제공합니다. 이 세트를 반복하려면 HashSet 인스턴스의 크기 (요소 수)와 지원 HashMap 인스턴스의 "용량"(버킷 수)의 합계에 비례하는 시간이 필요합니다. 따라서 반복 성능이 중요한 경우 초기 용량을 너무 높게 (또는 부하 계수가 너무 낮게) 설정하지 않는 것이 매우 중요합니다.

    반복 할 때 HashSet생성 된 요소의 순서는 정의되지 않습니다.

  • LinkedHashSet

    예측 가능한 반복 순서를 사용하는 Set 인터페이스의 해시 테이블 및 연결 목록 구현. 이 구현은 모든 항목을 통해 실행되는 이중 연결 목록을 유지한다는 점에서 HashSet과 다릅니다. 이 연결 목록은 요소가 집합에 삽입 된 순서 인 반복 순서를 정의합니다 (삽입 순서). 요소가 세트에 다시 삽입되는 경우 게재 순서는 영향을받지 않습니다. (s.contains (e)가 호출 직전에 true를 반환 할 때 s.add (e)가 호출되면 요소 e가 집합 s에 다시 삽입됩니다.)

    따라서 위 코드의 출력은 ...

     Set<Integer> linkedHashSet = new LinkedHashSet<>();
     linkedHashSet.add(3);
     linkedHashSet.add(1);
     linkedHashSet.add(2);
    
     for (int i : linkedHashSet) {
         System.out.println(i);
     }

    ... 필연적으로

    3
    1
    2
  • TreeSet

    이 구현은 기본 작업 (추가, 제거 및 포함)에 대해 보장 된 log (n) 시간 비용을 제공합니다. 기본적으로 반복에서 반환되는 요소는 " 자연 순서 " 로 정렬 되므로 위의 코드는 ...

     Set<Integer> treeSet = new TreeSet<>();
     treeSet.add(3);
     treeSet.add(1);
     treeSet.add(2);
    
     for (int i : treeSet) {
         System.out.println(i);
     }

    ... 다음을 출력합니다.

    1
    2
    3

    ( 생성자에 Comparator인스턴스를 전달 TreeSet하여 요소를 다른 순서로 정렬 할 수도 있습니다 .)

    Set 인터페이스를 올바르게 구현하려면 집합에 의해 유지되는 순서 (명시 적 비교기가 제공되는지 여부에 관계없이)가 equals와 일치해야합니다. (같음과 일치에 대한 정확한 정의는 Comparable 또는 Comparator를 참조하십시오.) 이것은 Set 인터페이스가 같음 연산의 관점에서 정의되었지만 TreeSet 인스턴스는 compareTo (또는 비교) 메서드를 사용하여 모든 요소 비교를 수행하기 때문입니다. 이 방법에 의해 동일하다고 간주되는 요소는 집합의 관점에서 동일합니다. 집합의 동작은 순서가 같음과 일치하지 않더라도 잘 정의되어 있습니다. Set 인터페이스의 일반 계약을 따르지 않습니다.


이제 혼란 스럽습니다. 어떤 것을 사용해야합니까? 고유 한 문자열 목록을 유지하기 만하면됩니다. 따라서 기본적으로 기존 문자열이 추가 되더라도 실제로 추가되어야합니다.

1
선택은 당신의 몫입니다 ... HashSet은 보편적이고 빠르며, 트리 셋은 정렬되어 있습니다 .LinkedHashset은 삽입 순서를 유지합니다 ...
Frank

6
이것은 LIST가 아니므로 모든 LIST 인터페이스 메서드를 사용할 수있는 것은 아닙니다.
marcolopes

2
집합은 목록이 아니므로 O (1) 시간 (랜덤 액세스)에서 집합의 인덱스로 요소를 조회 할 수 없습니다.
wilmol

13

나는 다른 사람들이 암시했지만 실제로 명시 적으로 언급하지 않은 원래 포스터에 대해 여기에서 몇 가지를 명확히하고 싶습니다. 고유 목록을 원한다고 말할 때, 이것이 바로 Ordered Set의 정의입니다. Set Interface와 List 인터페이스의 다른 주요 차이점은 List를 사용하여 삽입 인덱스를 지정할 수 있다는 것입니다. 따라서 질문은 목록 인터페이스가 정말로 필요합니까 (예 : 타사 라이브러리와의 호환성 등) 또는 Set 인터페이스를 사용하도록 소프트웨어를 재 설계 할 수 있습니까? 또한 인터페이스로 무엇을하는지 고려해야합니다. 인덱스로 요소를 찾는 것이 중요합니까? 세트에서 얼마나 많은 요소를 기대하십니까? 많은 요소를 갖게 될 경우 주문이 중요합니까?

유일한 제약 조건이있는 List가 정말로 필요한 경우에는 Apache Common Utils 클래스 org.apache.commons.collections.list.SetUniqueList가 있습니다.이 클래스는 List 인터페이스와 고유 제약 조건을 제공합니다. 그러나 이것은 List 인터페이스를 깨뜨립니다. 그러나 색인별로 목록을 검색해야하는 경우이 기능에서 더 나은 성능을 얻을 수 있습니다. Set 인터페이스를 다룰 수 있고 데이터 세트가 더 작은 경우 LinkedHashSet이 좋은 방법 일 수 있습니다. 소프트웨어의 디자인과 의도에 따라 다릅니다.

다시 말하지만, 각 컬렉션에는 특정 장단점이 있습니다. 일부는 빠른 삽입이지만 느린 읽기, 일부는 빠른 읽기이지만 느린 삽입 등입니다. 각 클래스 및 인터페이스의 세부 사항에 대해 완전히 배우려면 콜렉션 문서에 상당한 시간을 투자하는 것이 좋습니다.


3
이것은 질문에 대한 답을 제공하지 않습니다. 작성자에게 비평이나 설명을 요청하려면 게시물 아래에 댓글을 남겨주세요. 언제든지 자신의 게시물에 댓글을 달 수 있으며, 충분한 평판얻으면 모든 게시물댓글 수 있습니다 .
Zach Saucier 2014

1
실제로 답을 제공합니다. 그가 Set처럼 작동하는 목록을 원하면 org.apache.commons.collections.list.SetUniqueList를 사용하지만 프로그래머로서 그 / 우리는 그보다 더 조심해야하며 문제에 대해 더 많이 생각해야합니다. 이것이 내 대답이 더 나아지면 "Java에서 고유 목록을 만드는 방법?" 방법은 목록 uniqueList = 새로운 SetUniqueList (); ....
폴 코놀리

3
그리고 Zach, 나는 바보가 되려고하지 않지만 당신은 당신의 코멘트 전에 내 대답을 읽었습니까? 아니면 이해가 안 되나요? 이해가 안 되더라도 괜찮습니다. 알려 주시면 주제를 확장하겠습니다. 나는 누군가의 질문에 대한 친근한 답변을 제공하기 위해 데이터 구조에 대한 논문을 작성해야한다고 생각하지 않습니다. 또한 내가 답을 알고 있고 다른 사람이 실제로 제공하지 않았을 때 내 댓글 평판을 높이는 온유 한 방법을 사용하지도 않습니다.
Paul Connolly

1
그건 그렇고, 나는 저자에게 비판하거나 설명을 요청하지 않았습니다. 나는 그가 A) 내가 그에게 준 수업을 빨리 사용하거나 B) 시간을내어 이러한 수업의 차이점을 실제로 이해하고 관련시킬 수 있다고 말했습니다. 그의 필요에. B는 분명히 더 오래 걸리지 만 장기적으로 더 나은 코드를 만들 것입니다.
Paul Connolly

8

사용 new HashSet<String> 예 :

import java.util.HashSet;
import java.util.Set;

public class MainClass {
  public static void main(String args[]) {
    String[] name1 = { "Amy", "Jose", "Jeremy", "Alice", "Patrick" };

    String[] name2 = { "Alan", "Amy", "Jeremy", "Helen", "Alexi" };

    String[] name3 = { "Adel", "Aaron", "Amy", "James", "Alice" };

    Set<String> letter = new HashSet<String>();

    for (int i = 0; i < name1.length; i++)
      letter.add(name1[i]);

    for (int j = 0; j < name2.length; j++)
      letter.add(name2[j]);

    for (int k = 0; k < name3.length; k++)
      letter.add(name3[k]);

    System.out.println(letter.size() + " letters must be sent to: " + letter);

  }
}

2
위의 프로그램을 추가하기 만하면-> 11 개의 편지를 다음 주소로 보내야합니다. [Aaron, Alice, James, Adel, Jose, Jeremy, Amy, Alan, Patrick, Helen, Alexi]
Ammad

4

를 사용하여 HashSet<String>고유 한 개체 모음을 유지할 수 있습니다. Integer맵 의 값이 중요한 경우 대신 containsKey맵 메서드를 사용 하여 키가 맵에 이미 있는지 테스트 할 수 있습니다.


3

HashSet<String>(또는) 어떤 Set구현 이든 당신을 위해 일을 할 수 있습니다. Set중복을 허용하지 마십시오.

다음은 HashSet 용 javadoc 입니다.


2

이것이 얼마나 효율적인지 모르겠지만 간단한 맥락에서 나를 위해 일했습니다.

List<int> uniqueNumbers = new ArrayList<>();

   public void AddNumberToList(int num)
    {
        if(!uniqueNumbers .contains(num)) {
            uniqueNumbers .add(num);
        }
    }

1

java.util.Set<E>인터페이스 의 구현 클래스 ( 예 : java.util.HashSet<String> 컬렉션 클래스) 중 하나를 사용할 수 있습니다 .

중복 요소가없는 컬렉션입니다. 보다 공식적으로 집합에는 e1.equals (e2)와 같은 요소 e1 및 e2 쌍이없고 최대 하나의 null 요소가 포함됩니다. 이름에서 알 수 있듯이이 인터페이스는 수학적 집합 추상화를 모델링합니다.

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