Set 인터페이스
Set
은Collection
이다. 이것은 중복된 엘리먼트를 포함하지 않는 특징이 있다. 이것은 수학의 집합이라는 추상 명사에서 모델을 잡고 있다. Set 인터페이스는 컬렉션으로 부터 상속받은 메소드를 포함하고 있으며, 오직 동일한 엘리먼트가 중복으로 들어가지 않도록 방지하는 기능이 추가되어 있다. Set은 또한 equals과 hashCode 오퍼레이션에 대해서 매우 강력한 조항을 가지고 있다. 또한 Set 인스턴스에서는 구현된 타입이 서로 틀리다면 구분할 수 있도록 해주는 기능을 가진다. 두개의 Set 인스턴스가 서로 동일한 엘리먼트를 가지고 있다면 서로 동일한 것으로 판정한다.Set 인터페이스는 다음과 같은 내용을 포함한다.
자바 플랫폼에서는 3개의 일반적은 Set 구현체를 가지고 있는데 HashSet, TreeSet, LinkedHashSet이 그것이다.public interface Set<E> extends Collection<E> {
// Basic operations
int size();
boolean isEmpty();
boolean contains(Object element);
boolean add(E element); //optional
boolean remove(Object element); //optional
Iterator<E> iterator();
// Bulk operations
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c); //optional
boolean removeAll(Collection<?> c); //optional
boolean retainAll(Collection<?> c); //optional
void clear(); //optional
// Array Operations
Object[] toArray();
<T> T[] toArray(T[] a);
}
HashSet
: 해시 테이블에 엘리먼트를 저장한다. 이것은 Set에서 최상의 성능을 자랑한다. 그러나 이것은 Iteration에서 순서에 대해서는 보장하지 않는다.
TreeSet
: 엘리먼트는 red-black 트리 자료구조로 저장된다. 순서는 해당 값에 따라서 정렬이 되며, 이것은 HashSet에 비해서 대채로 느린 속도를 낸다.
LinkedHashSet :
해시 테이블에 존재하는 자료를 저장할때 링크드 리스트를 이용하여 저장하며, 정렬 순서는 삽입된 순서대로 정렬이 이루어 진다. LinkedHashSet은 입력된 순서로 저장이 되기 때문에 해당 클라이언트에 의해서 저장될때 특정한 규칙이 없다면 혼돈된 순서 때문에 코스트가 HashSet보다 더 증가할 수 있는 특성이 있다.여기에는 단순하지만 매우 유용한 Set 처리 내용이 있다. Collection c라는 컬렉션을 가지고 있고, 다른 컬렉션을 생성하기를 원하면서 동일한 엘리먼트를 제거하고 싶은경우 다음과 같이 처리하면 된다. 이 한라인만 잇으면 그것이 수행된다.
이 작업은 Set을 생성하는 것이다. (정의에 의해서 중복은 존재할 수 없다.) 초기화에서 모든 엘리먼트를 저장하게 된다. 이것은 The Collection Interface 에서 설명한 기본적인 Converting 작업이다.Collection<Type> noDups = new HashSet<Type>(c);
다음으로 유용한 것은 원본 Collection에서 순서가 중요한 엘리먼트들을 중복을 제거하여 새로운 셋을 생성한다.
Collection<Type> noDups = new LinkedHashSet<Type>(c);
다음은 일반적인 메소드로 이전 기능을 만들었을때이다. 이것은 Set을 반환한다.
public static <E> Set<E> removeDups(Collection<E> c) {
return new LinkedHashSet<E>(c);
}
Set 인터페이스의 기본적인 처리
size 오퍼레이션은 Set에 포함되어 있는 (cardinality)엘리먼트의 개수를 반환한다.
isEmpty 메소드는 무엇을 말하는지 설명하지 않아도 알것이다. add메소드는 특정 엘리먼트를 Set에 추가한다. 반환되는 값에 따라서 추가하는 작업이 성공되었는지 확인할 수 있다. 유사하게 remove 메소드는 특정 엘리먼트를 Set에서 제거한다. 반환되는 값을 통해 정상적으로 제거 되었는지 확인 할 수 있다. iterator 메소드는 Iterator 객체를 Set으로 부터 반환한다.다음
program
은 아규먼트로 들어온 값들에서 중복을 제거하는 일을 수행한다. 그리고 개별 단어들의 개수를 반환하고, 중복이 제거된 엘리먼트 리스트를 반환한다.
실행 방법 :import java.util.*;
public class FindDups {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
for (String a : args)
if (!s.add(a))
System.out.println("Duplicate detected: " + a);
System.out.println(s.size() + " distinct words: " + s);
}
}
실행 결과 :java FindDups i came i saw i left
중요한 것은 Collection은 구현된 HashSet을 참조하는 것이 아니라. 자신의 해당 타입 (Set)을 참조하고 있다는 것이다. 이것은 매우 추천하는 프로그램 습관이며, 이것은 좀더 구현의 변화에 대해서 유연하게 대처하도록 해준다. 만약 인터페이스를 전달하지 않고, 구현된 객체를 전달하는 경우 하나를 변경하면 그와 관련된 다른 구현타입되 변경해줘야 하는 어려움이 발생한다.Duplicate detected: i
Duplicate detected: i
4 distinct words: [i, left, saw, came]
더우기 프로그램이 수행하는 결과에 대해서 개런티 할 수 없는 상황이 발생할 수 있다. 만약 프로그램이 원래 구현되어 있는 처리 대신에 비표준적인 처리를 수행하고 있다면 프로그램을 실패할 것이다.
Set의 구현 타입으로 HashSet의 예를 확인해 보면, 이것은 엘리먼트의 순서를 보장하지 않는다. 만약 알파벳 순서로 출력하기를 원한다면 단지 Set의 구현을 HashSet에서 TreeSet으로 변경하면 된다. 한라인의 단순한 변환으로 다음과 같은 처리를 수행하고, 출력을 볼 수 있다.
java FindDups i came i saw i left
Duplicate detected: i
Duplicate detected: i
4 distinct words: [came, i, left, saw]
Bulk 처리를 수행하는 Set 인터페이스
Bulk 오퍼레이션은 특히 Set 처리에 적합하다. 일반적으로 선형 대수 처리값에 해당하는 성능을 나타낸다.
s1과 s2가 설정되어 있다면 다음과 같은 벌크 오퍼레이션을 수행할 수 있다.union, intersection이나 두개의 셋의 차이에 대한 것을 계산할때에 두개의 모든 셋을 변경하지 않고자 한다면 다음과 같이 카피 작업을 수행한 후에 벌크 오퍼레이션을 적용해야 한다. 다음은 이러한 처리를 나타내는 코드이다.
s1.containsAll(s2)
— s2가 s1의 하위 셋이면 true를 반환한다. (s2의 모든 엘리먼트를 s1이 가지고 있다면 s2는 s1의 서브셋이 된다.)s1.addAll(s2)
— s1에 s2의 셋을 union으로 변환한다. (두개의 셋의 유니온은 각 셋의 모든 엘리먼트를 포함한다는 것을 의미한다.)s1.retainAll(s2)
— s1 과 s2의 인터섹션값으로 s1을 변환한다. (두개의 셋의 인터섹션은 두 셋에서 모두 포함된 항목을 말한다.)s1.removeAll(s2)
— s1과 s2의 서로 차이가 나는 값들만 s1에 들어가도록 한다. (예를 들어 s1 - s2는 s1에는 포함되나 s2에는 포함되지 않는 값들의 집합을 말한다.)Set 타입의 구현에서 HashSet에 대한 사항은 앞에서 이미 언급했다. Set중 가장 최고의 처리를 수행한다는 것을 알 것이다. 그러나 목적에 따라서 적합한 Set의 구현을 적용하면 될 것이다.Set<Type> union = new HashSet<Type>(s1);
union.addAll(s2);
Set<Type> intersection = new HashSet<Type>(s1);
intersection.retainAll(s2);
Set<Type> difference = new HashSet<Type>(s1);
difference.removeAll(s2);
FindDups 프로그램을 다시한번 보자. 입력된 단어를 오직 한번 보이는 작업과, 중복된 단어만 보여주는 작업을 모두 실행하고자 한다. Here's how
the resulting program
looks.동일한 아규먼트를 이전에 넣었던것과 같이 (import java.util.*;
public class FindDups2 {
public static void main(String[] args) {
Set<String> uniques = new HashSet<String>();
Set<String> dups = new HashSet<String>();
for (String a : args)
if (!uniques.add(a))
dups.add(a);
// Destructive set-difference
uniques.removeAll(dups);
System.out.println("Unique words: " + uniques);
System.out.println("Duplicate words: " + dups);
}
}
i came i saw i left
)로 넣어주면 프로그램은 다음과 같은 내용을 출력할 것이다.공통적인 집합 대수 연산은 symmentric set difference이다. 두개의 특정 셋에 포함된 엘리먼트 셋으로 둘다에 포함되어 있지 않다. 그런경우 다음과 같은 연산 처리를 이용하여 두개의 셋의 차이점을 set의 특성을 깨지 않고 처리할 수 있다.Unique words: [left, saw, came]
Duplicate words: [i]
Set<Type> symmetricDiff = new HashSet<Type>(s1);
symmetricDiff.addAll(s2);
Set<Type> tmp = new HashSet<Type>(s1);
tmp.retainAll(s2));
symmetricDiff.removeAll(tmp);
Array Operations을 처리하기 위한 Set Interface
array 처리는 다른 Collection 처리와 같이 특별한것이 없다. 이 오퍼레이션은 Collection Interface section 에서 확인해 보면 된다.