수학의 집합 개념을 따른다(순서가 없고, 중복이 없음)
1) 저장 순서가 유지되지 않는다. 즉 저장할 때의 순서와 찾을 때의 순서가 다르다.
2) 객체를 중복 저장할 수 없다.
3) 하나의 null만 저장할 수 있다.
Set 컬렉션에서 공통으로 사용할 수 있는 Set 인터페이스의 메소드.
Set 인터페이스는 제네릭 타입이다.
1) 객체 추가
2) 객체 검색
3) 객체 삭제
//HashSet으로 예시
Set<String> set = new HashSet<String>();
set.add("apple");
set.add("banana");
set.remove("apple");
Set 컬렉션은 인덱스로 객체를 검색해 가져오는 메소드가 없는 대신, 전체 객체를 대상으로 한번씩 반복해서 가져오는 Iterator(반복자)를 제공한다.
Iterator는 iterator() 메소드를 호출해 얻는다.
Set<String> set = new HashSet<String>();
Iterator<String> iterator = set.iterator();
Set<String> set = new HashSet<String>();
Iterator<String> iterator = set.iterator();
while(iterator.hashNext()) { //객체가 있으면 가져온다. 저장된 객체의 수만큼 loop한다
String str = iterator.next();
}
Iterator의 remove()메소드를 이용해도 실제 Set 컬렉션에서 객체가 제거된다.
while(iterator.hasNext()) {
String str = iterator.next();
if(str.equals("banana")) {
iterator.remove();
}
}
또는 향상된 for문을 사용해 전체 객체를 대상으로 반복할 수 있다.
Set<String> set = new HashSet<String>();
for(String str : set) {
...;
}
Set 인터페이스의 구현 클래스이다. 따라서 객체들을 순서없이 저장하고 중복 저장하지 않는다.
Set<E> set = new HashSet<E>();
HashSet이 동일한 객체로 판단하는 기준은 같은 인스턴스를 의미하지 않는다.
(1) add() 메소드 호출로 객체를 추가하려 함
(2) HashSet은 객체의 hashCode() 메소드를 호출해 해시코드 리턴값을 알아낸다
(3) 저장되어 있는 객체들의 해시코드값과 다를 경우 다른 객체로 간주하여 저장하고, 동일할 경우 다음 과정을 진행한다.
(4) HashSet의 equals() 메소드를 호출하여 false가 리턴되면 다른 객체로 간주하여 저장하고, true가 리턴될 경우 같은 객체라 판한하여 저장하지 않는다.
즉, HashCode() 리턴값이 같고 equals() 리턴값이 같으면 같은 객체로 간주한다.
String 클래스는 hashCode()와 equals() 메소드를 재정의해서 같은 문자열일 경우 hashCode()의 리턴값이 같고, equals() 리턴값이 true이도록 정의했다.
따라서 HashSet을 이용할 때, 같은 문자열을 갖는 String 객체는 동등한 객체로 간주된다.
만일 과일 이름과 개수가 동일하면 동일한 객체로 간주하여 중복 저장되지 않도록 하는 사용자 정의 클래스 Fruit이 있다면 다음과 같을 것이다.
public class Fruit {
public String name;
public int fruitCnt;
public Fruit(String name, int fruitCnt) {
this.name = name;
this.fruitCnt = fruitCnt;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Fruit) {
Fruit fruit = (Fruit) obj;
return fruit.name.equals(name) && (member.fruitCnt == fruitCnt); //string 클래스의 equals메소드를 이용해, name과 fruitCnt가 동일하면 true 리턴
} else {
return false;
}
}
@Override
public int hashCode() { //name과 fruitCnt가 동일하면 동일한 hashCode를 리턴하도록 한다
return name.hashCode() + fruitCnt;
}
}
TreeSet은 검색 기능을 강화시킨 Set의 컬렉션이다.
(1) Binary Tree(이진 트리) 사용
(2) 계층 구조(Tree 구조)를 유지하며 객체 저장
이진 트리는 부모 노드의 값보다 작은 노드는 왼쪽에 위치 시키고, 부모 노드의 값보다 큰 노드는 오른쪽에 위치시킨다.
객체 추가시, 루트 노드부터 시작해 값의 크기를 비교하며 잎노드를 향해 내려간다.
문자를 저장할 경우에는 문자의 유니코드 값으로 비교한다.
따라서 가장 왼쪽에 위치한 노드가 제일 작은 값이 되고, 가장 오른쪽에 위치한 노드가 제일 큰 값이 된다.
하나의 노드는 노드값인 value와 왼쪽 자식 노드와 오른쪽 자식 노드를 참조하기위한 두 개의 변수로 구정된다. TreeSet에 객체를 저장하면 자동으로 이진트리 구조에 맞게 정렬된다.
TreeSet<E> treeSet = new TreeSet<E>();
객체 검색과 범위 검색과 관련된 메소드를 사용할 때, Set 인터페이스 타입 변수에 대입하는것 보다 TreeSet 클래스 타입으로 대입하는것이 훨씬 효율적이다.
Iterator<E> descendingIterator()
NavigableSet<E> descendingSet()
NavigableSet은 TreeSet과 동일하게 first(), last(), lower(), higher(), floor(), ceiling() 메소드를 제공하며, 정렬 순서를 바꾸는 descendingSet() 메소드를 제공한다.
따라서 treeSet을 오름차순으로 정렬하고 싶을 경우 descendingSet() 메소드를 두 번 호출해 사용할 수 있다.NavigableSet<E> descendingSet = treeSet.descendingSet(); NavigableSet<E> ascendingSet = descendingSet.descendingSet();
NavigableSet<E> headSet(E toElement, boolean inclusive)
inclusive가 true일 경우 : 찾는 객체 <= toElement
inclusive가 flase일 경우 : 찾는 객체 < toElement
NavigableSet<E> tailSet(E fromElement, boolean inclusive)
inclusive가 true일 경우 : fromElement <= 찾는 객체
inclusive가 flase일 경우 : fromElement < 찾는 객체
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
fromInclusive : true일 경우 시작 객체 포함
toInclusive : true일 경우 끝 객체 포함
<범위 관련 메소드들 예제>
public class TreeSetEx {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
int[] numbers = {9, 5, 2, 1, 4, 10};
for(int i=0; i<numbers.length; i++) {
treeSet.add(new Integer(numbers[i]));
}
NavigableSet<Integer> headSet = treeSet.headSet(new Integer(5), true);
NavigableSet<Integer> tailSet = treeSet.tailSet(new Integer(5), false);
NavigableSet<Integer> subSet = treeSet.subSet(new Integer(4), true, new Integer(10), false);
//향상된 for문을 사용해 전체 객체 반복하기
System.out.println("headSet 결과");
for(Integer num : headSet) {
System.out.print(num + " ");
}
System.out.println();
System.out.println("tailSet 결과");
for(Integer num : tailSet) {
System.out.print(num + " ");
}
System.out.println();
System.out.println("subSet 결과");
for(Integer num : subSet) {
System.out.print(num + " ");
}
}
}
/* 실행 결과
headSet 결과
1 2 4 5
tailSet 결과
9 10
subSet 결과
4 5 9
*/