Set Collection

hyekyeong Song·2020년 5월 3일
1

1.특징

수학의 집합 개념을 따른다(순서가 없고, 중복이 없음)
1) 저장 순서가 유지되지 않는다. 즉 저장할 때의 순서와 찾을 때의 순서가 다르다.
2) 객체를 중복 저장할 수 없다.
3) 하나의 null만 저장할 수 있다.

2. Set Interface methods

Set 컬렉션에서 공통으로 사용할 수 있는 Set 인터페이스의 메소드.
Set 인터페이스는 제네릭 타입이다.

1) 객체 추가

  • boolean add(E e) : 주어진 객체를 저장, 객체가 성공적으로 저장되면 true리턴. 중복 객체일 경우 false 리턴.

2) 객체 검색

  • boolean contains(Object o) : 주어진 객체가 저장되어있는지 검색
  • isEmpty() : 빈 컬렉션인지 검사
  • int size() : 저장되어 있는 전체 객체의 수를 리턴

3) 객체 삭제

  • void clear() : 저장된 모든 객체를 삭제
  • boolean remove(Object o) : 주어진 객체를 삭제

객체 저장/삭제

//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();

Iterator 인터페이스의 메소드들

  • boolean hasNext() : 가져올 객체가 있으면 true, 없으면 false리턴
  • E next() : 컬렉션에서 하나의 객체를 가져온다
  • void remove() : Set 컬렉션에서 객체를 제거한다
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문 사용

또는 향상된 for문을 사용해 전체 객체를 대상으로 반복할 수 있다.

Set<String> set = new HashSet<String>();
for(String str : set) {
	...;
}

3. HashSet

Set 인터페이스의 구현 클래스이다. 따라서 객체들을 순서없이 저장하고 중복 저장하지 않는다.

1) 생성

Set<E> set = new HashSet<E>();

2) HashSet의 동일 객체

HashSet이 동일한 객체로 판단하는 기준은 같은 인스턴스를 의미하지 않는다.

(1) add() 메소드 호출로 객체를 추가하려 함
(2) HashSet은 객체의 hashCode() 메소드를 호출해 해시코드 리턴값을 알아낸다
(3) 저장되어 있는 객체들의 해시코드값과 다를 경우 다른 객체로 간주하여 저장하고, 동일할 경우 다음 과정을 진행한다.
(4) HashSet의 equals() 메소드를 호출하여 false가 리턴되면 다른 객체로 간주하여 저장하고, true가 리턴될 경우 같은 객체라 판한하여 저장하지 않는다.

즉, HashCode() 리턴값이 같고 equals() 리턴값이 같으면 같은 객체로 간주한다.

3) String 클래스의 hashCode(), equals() 메소드 재정의

String 클래스는 hashCode()와 equals() 메소드를 재정의해서 같은 문자열일 경우 hashCode()의 리턴값이 같고, equals() 리턴값이 true이도록 정의했다.
따라서 HashSet을 이용할 때, 같은 문자열을 갖는 String 객체는 동등한 객체로 간주된다.

4) 메소드 오버라이딩을 통한 동일 객체 판단 기준 재정의

만일 과일 이름과 개수가 동일하면 동일한 객체로 간주하여 중복 저장되지 않도록 하는 사용자 정의 클래스 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;
    }
}

4. TreeSet - 검색 기능을 강화시킨 Set Collection

TreeSet은 검색 기능을 강화시킨 Set의 컬렉션이다.

1) 특징

(1) Binary Tree(이진 트리) 사용
(2) 계층 구조(Tree 구조)를 유지하며 객체 저장

2) 이진 트리

이진 트리는 부모 노드의 값보다 작은 노드는 왼쪽에 위치 시키고, 부모 노드의 값보다 큰 노드는 오른쪽에 위치시킨다.
객체 추가시, 루트 노드부터 시작해 값의 크기를 비교하며 잎노드를 향해 내려간다.
문자를 저장할 경우에는 문자의 유니코드 값으로 비교한다.
따라서 가장 왼쪽에 위치한 노드가 제일 작은 값이 되고, 가장 오른쪽에 위치한 노드가 제일 큰 값이 된다.

  • 오름차순으로 정렬된 값을 얻고 싶을 경우 : 트리 중위순회(inorder)를 하면된다. <왼쪽 서브트리, 부모노드, 오른쪽 서브트리 순서로 순회>
  • 내림차순으로 정렬된 값을 얻고 싶을 경우 : <오른쪽 서브트리, 부모노드, 오른쪽 서브트리 순서로 순회>

3) 구조

하나의 노드는 노드값인 value와 왼쪽 자식 노드와 오른쪽 자식 노드를 참조하기위한 두 개의 변수로 구정된다. TreeSet에 객체를 저장하면 자동으로 이진트리 구조에 맞게 정렬된다.

  • TreeSet 생성
TreeSet<E> treeSet = new TreeSet<E>();

(1) 검색 관련 메소드들

객체 검색과 범위 검색과 관련된 메소드를 사용할 때, Set 인터페이스 타입 변수에 대입하는것 보다 TreeSet 클래스 타입으로 대입하는것이 훨씬 효율적이다.

  • E first() : 제일 낮은(트리에서 가장 왼쪽에 위치한) 객체를 리턴
  • E last() : 제일 높은(트리에서 가장 오른쪽에 위치한) 객체를 리턴
  • E lower(E e) : 주어진 객체보다 바로 아래의 객체를 리턴
  • E higher(E e) : 주어진 객체보다 바로 위의 객체를 리턴
  • E floor(E e) : 주어진 객체와 동등한 객체가 있으면 리턴, 만약 없다면 주어진 객체의 바로 아래의 객체를 리턴
  • E ceiling(E e) : 주어진 객체와 동등한 객체가 있으면 리턴, 만약 없다면 주어진 객체 바로 위의 객체를 리턴
  • E pollFirst() : 제일 낮은 객체를 꺼내고 컬렉션에서 제거
  • E pollLast() : 제일 높은 객체를 거내오고 컬렉션에서 제거

(2) 정렬 관련 메소드들

  • 내림차순으로 정렬된 Iterator 리턴
Iterator<E> descendingIterator()
  • 내림차순으로 정렬된 NavigableSet 리턴
NavigableSet<E> descendingSet()

NavigableSet은 TreeSet과 동일하게 first(), last(), lower(), higher(), floor(), ceiling() 메소드를 제공하며, 정렬 순서를 바꾸는 descendingSet() 메소드를 제공한다.
따라서 treeSet을 오름차순으로 정렬하고 싶을 경우 descendingSet() 메소드를 두 번 호출해 사용할 수 있다.

  NavigableSet<E> descendingSet = treeSet.descendingSet();
  NavigableSet<E> ascendingSet = descendingSet.descendingSet();

(3) 범위 검색 관련 메소드들

  • 주어진 객체보다 낮은 객체들을 NavigableSet으로 리턴한다.
NavigableSet<E> headSet(E toElement, boolean inclusive)

inclusive가 true일 경우 : 찾는 객체 <= toElement
inclusive가 flase일 경우 : 찾는 객체 < toElement

  • 주어진 객체보다 높은 객체들을 NavigableSet으로 리턴한다
NavigableSet<E> tailSet(E fromElement, boolean inclusive)

inclusive가 true일 경우 : fromElement <= 찾는 객체
inclusive가 flase일 경우 : fromElement < 찾는 객체

  • 시작과 끝으로 주어진 객체 사이의 객체들을 NavigableSet으로 리턴한다.
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 
*/
profile
안녕하세요😀😀

0개의 댓글