본문 바로가기
JAVA

Set 컬렉션 (HashSet, TreeSet)

by winteringg 2022. 8. 28.

1. Set 컬렉션의 특징 및 주요 메서드
1) 특
  - 수학의 집합과 유사한 개념.
  - 저장 순서가 유지되지 않고, 중복도 허용하지 않음. ex) 양의 정수집합, 소수의 집합 등
  - 하나의 null 만 저장할 수 있음.
  - 인덱스로 객체를 검색해 가져오는 메서드가 없으므로 반복자(Iterator)를 제공.


2) 구현 클래스
  - HashSet
  - LinkedHashSet
  - TreeSet

3) 주요 메서드 (※저장 순서가 유지되지 않기 때문에 인덱스를 검색해서 가져오는 메서드는 없음.)

기능 메서드 설명
객체 추가 boolean add(E e) 주어진 객체 추가
객체 검색 boolean contains(Object o) 주어진 객체가 저장되어 있는지 여부에 따라 true / false 를 리턴
boolean isEmpty() 컬렉션 전체가 비어 있는 지 조사해서 true, false 로 리턴
Iterator<E> iterator() 저장된 객체를 한 번씩 가져오는 반복자 리턴
int size() 저장되어 있는 전체 객체 수를 리턴
객체 삭제 void clear() 저장된 모든 객체를 삭제
boolean remove(Object o) 주어진 객체를 삭제
객체 배열 Object[] toArray() Set에 저장된 객체를 배열로 반환
Object[] toArray(Object[] a)  

  - Iterator 반복자 사용법

Object[] objArr = { "1", "2", "3", "4", "5" }
Set set = new HashSet();

//배열을 set 에 추가
for(int i=0; i<objArr.length, i++) {
   set.add(ObjArr[i]);  //HashSet에 objArr의 요소들을 저장.
   
   Iterator it = set.iterator();
   
   while(it.hasNext()) {   //읽어올 요소가 있는지 확인 후,
      System.out.println(it.next());  //요소 하나씩 꺼내오기


  - Set 컬렉션 객체 추가, 찾기, 삭제

Set<String>list = ....; //Set이 저장하는 객체 타입이 String 타입이라는 의미
set.add("홍길동");      //객체 추가
set.add("김자바");      //객체 추가
set.remove("홍길동");   //객체 삭제


2. HashSet
1) 내부 구조 표현 방법

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

2) 순서 유지를 하고 싶으면 LinkedHashSet 클래스를 사용
3) 순서를 유지하지 않고 중복도 허용하지 않음.
  - 객체가 다르더라도 동등하다고 판단되면 중복 저장 하지 않음.

4) 동일한 객체인지 판단하는 방법

동등 객체 판단 방법

  - HashSet 사용

Set set = new HashSet();

//set의 크기가 6보다 작은 동안 1~45 사이의 난수를 저장
for(int i=0; set.size()<6, i++) {
  int num = (int)(Math.random()*45)+1;
  set.add(num);
}
System.out.println(set);   //정렬되지 않은 숫자들이 랜덤으로 출력됨
List list = new LinkedList(set);  //LinkedList(Collection c) set을 리스트로 옮긴 후,
Collections.sort(list);           //Collections.sort(List list) 숫자를 정렬
System.out.println(list);  //오름차순으로 정렬된 숫자들이 출력됨


  - HashSet은 객체를 저장하기 전에 기존에 같은 객체가 있는지 확인. 같은 객체가 없으면 저장하고, 있으면 저장하지 않음
  - boolean add(Object o)는 저장할 객체의 equals()와 hashCode()를 호출. equals()와 hashCode()가 오버라이딩 되어 있어야 함. (아래 예시 참고)

HashSet set = new HashSet();

set.add("abc");
set.add("abc");  //중복이라 저장 안됨.
set.add("new Person("David,10"); //중복인데도 불구하고 저장 되어 출력됨.
set.add("new Person("David,10"); //새로 생성한 Person 객체에서 equals(), hashCode()가 오버라이딩 안되어서.
                                  
System.out.println(set);   //출력: [abc, David:10, David:10]


//equals(), hashCode()를 오버라이딩 해야 HashSet이 바르게 동작.
class Person {
   String name;
   int age;
   
   Person(String name, int age) {
       this.name = name;
       this.age = age;
   }
   
   Public String toString() {
       return name +":"+ age;
   }

  - equals(), hashCode() 오버라이딩 후

HashSet set = new HashSet();

set.add("abc");
set.add("abc");  //중복이라 저장 안됨.
set.add("new Person("David,10"); //equals(), hashCode()가 오버라이딩 되어 있어서 중복으로 처리.
set.add("new Person("David,10");
                                  
System.out.println(set);   //출력: [abc, David:10]


//equals(), hashCode()를 오버라이딩 해야 HashSet이 바르게 동작.
class Person {
   String name;
   int age;
   
   @Override
   public int hashCode() {
       //int hash(Object... values);  //... 가변인자라서 어떤 매개변수를 넣어도 됨.
       return Objects.hash(name, age);
   }
   
   @Override
   public boolean equals(Object obj) {
       if(!(obj instanceof Persron)) return false;
       
       Person p = (Person)obj;
       //나 자신(this)의 이름과 나이를 p와 비교
       return this.name.equals(p.name) && this.age == p.age;
   }
   
   Person(String name, int age) {
       this.name = name;
       this.age = age;
   }
   
   Public String toString() {
       return name +":"+ age;
   }

 

3. TreeSet
1) 객체 생성 구조

TreeSet<E> treeSet = new TreeSet<E>();

 

2) 트리 형태의 부모 노드와 자식 노드로 구성된 '이진 탐색 트리 (binary search tree)'를 이용하여 계층적 구조(tree 구조)를 가지면서 객체를 저장함. 범위 검색과 정렬에 유리함.
  - 이진 트리 (binary tree) : 부모 노드에 최대 2개의 자식 노드를 연결할 수 있는 것.
  - 이진 탐색 트리 (binary search tree) : 이진 트리의 하위개념. 부모보다 작은 값은 왼쪽, 큰 값은 오른쪽에 저장됨.
    - 단점 : 데이터가 많아질 수록(트리가 커질수록) 추가, 삭제에 시간이 더 걸림. (비교 횟수 증가)
※ 참고 : 트리 순회(tree traversal)
  - 이진 트리의 모든 노드를 한 번씩 읽는 것.
  - 전위, 중위, 후위 순외법이 있으며, 중위 순외하면 오름차순으로 정렬됨.

이진 트리

3) TreeSet에 객체를 저장하면 부모값과 비교해 자동으로 정렬됨.
4) 노드는 노드 값과 왼쪽과 오른쪽 자식을 참조하기 위한 두 개의 변수로 구성

노드 값과 왼쪽과 오른쪽 자식을 참조하기 위한 두 개의 변수로 구성되는 TreeSet


5) 주요 메서드
  - 검색 메서드 : first(), last(), lower(), higher(), floor(), ceiling(), pollFirst(), pollLast()
  - 정렬 메서드 : descendingIterator(), descendingSet()
  - 범위 검색 메서드 : headSet(), tailSet(), subSet()

6) 메서드 구현하기

  - 검색 메서드 : first(), last(), lower(), higher(), floor(), ceiling() 활용

public class TreeSetExample1 {

	public static void main(String[] args) {
		TreeSet<Integer> scores = new TreeSet<Integer>();
		
		scores.add(87);
		scores.add(98);
		scores.add(75);
		scores.add(95);
		scores.add(80);
		
		Integer score = null;
		
		score = scores.first();  //가장 왼쪽에 있는 객체를 얻어서 리턴
		System.out.println("가장 낮은 점수 : " + score);
		
		score = scores.last();   //가장 오른쪽에 있는 객체를 얻어서 리턴
		System.out.println("가장 높은 점수 : " + score);
		
		score = scores.lower(95);  //95 보다 더 낮은 객체를 리턴
		System.out.println("95점 아래 점수 : " + score);
		
		score = scores.higher(95);  //95 보다 더 높은 객체를 리턴
		System.out.println("95점 위의 점수 : " + score);
		
                //95가 있으면 95 리턴, 95가 없으면 95 바로 아래의 객체 리턴
		score = scores.floor(95);
		System.out.println("95점이거나 바로 아래 점수 : " + score);
		
                //85가 있으면 85 리턴, 85가 없으면 85 바로 위의 객체 리턴
		score = scores.ceiling(85);
		System.out.println("85점 위의 점수 : " + score);
	}
}

출력 화면

가장 낮은 점수 : 75
가장 높은 점수 : 98
95점 아래 점수 : 87
95점 위의 점수 : 98
95점이거나 바로 아래 점수 : 95
85점 위의 점수 : 87

  - 검색 메서드 : pollFirst() 활용

public class TreeSetExample1 {

	public static void main(String[] args) {
		TreeSet<Integer> scores = new TreeSet<Integer>();
		
		scores.add(87);
		scores.add(98);
		scores.add(75);
		scores.add(95);
		scores.add(80);
		
		Integer score = null;
		
		while(!scores.isEmpty()) {
               //제일 작은 값인 왼쪽 자식 노드부터 하나씩 빼서 treeset에서 제거한 후 저장.
		score = scores.pollFirst();
		System.out.println(score+ " 를 빼면 남는 객체 수 : "+scores.size());
		}

출력 화면

//제일 작은 값인  왼쪽 자식 노드부터 하나씩 빼서 treeset에서 제거한 후 저장하기 때문에,
//최종 남는 객체는 0이 됨.
75 를 빼면 남는 객체 수 : 4
80 를 빼면 남는 객체 수 : 3
87 를 빼면 남는 객체 수 : 2
95 를 빼면 남는 객체 수 : 1
98 를 빼면 남는 객체 수 : 0

  - 검색 메서드 : pollLast() 활용

public class TreeSetExample1 {

	public static void main(String[] args) {
		TreeSet<Integer> scores = new TreeSet<Integer>();
		
		scores.add(87);
		scores.add(98);
		scores.add(75);
		scores.add(95);
		scores.add(80);
		
		Integer score = null;
		
		while(!scores.isEmpty()) {
               //제일 큰 값인 오른쪽 자식 노드부터 하나씩 빼서 treeset에서 제거한 후 저장.
		score = scores.pollLast();
		System.out.println(score+ " 를 빼면 남는 객체 수 : "+scores.size());
		}

출력 화면

//제일 큰 값인 오른쪽 노드부터 하나씩 빼서 treeset에서 제거한 후 저장하기 때문에,
//최종 남는 객체는 0이 됨.
98 를 빼면 남는 객체 수 : 4
95 를 빼면 남는 객체 수 : 3
87 를 빼면 남는 객체 수 : 2
80 를 빼면 남는 객체 수 : 1
75 를 빼면 남는 객체 수 : 0

- 정렬 메서드 : descendingSet() 활용

public class TreeSetExample1 {

	public static void main(String[] args) {
		TreeSet<Integer> scores = new TreeSet<Integer>();
		
		scores.add(87);
		scores.add(98);
		scores.add(75);
		scores.add(95);
		scores.add(80);
		
                 //내림차순으로 정렬
		NavigableSet<Integer> descendingSet = scores.descendingSet();
		for(Integer score : descendingSet) {
			System.out.print(score + " ");
		}
                  System.out.println();
                  System.out.println();
		
		//descendingSet.descendingSet 의미 : 내림차순에 한번 더 내림차순을 하게되면 올림차순이 됨.
		NavigableSet<Integer> ascendingSet = descendingSet.descendingSet();
		for(Integer score : ascendingSet) {
			System.out.print(score + " ");
		}
		System.out.println();
	}
}

출력 화면

98 95 87 80 75  //올림차순

75 80 87 95 98  //내림차순

 - 범위 검색 메서드 : subSet() 활용

public class TreeSetExample1 {

	public static void main(String[] args) {
		TreeSet<String> treeSet = new TreeSet<String>();

		treeSet.add("apple");
		treeSet.add("forever");
		treeSet.add("description");
		treeSet.add("ever");
		treeSet.add("zoo");
		treeSet.add("base");
		treeSet.add("guess");
		treeSet.add("cherry");
		
		System.out.println("[c~f 사이의 단어 검색]");
		//시작하는 값, 유무 여부, 끝나는 값, 유무 여부 확인해서 정렬해주는 subSet()메서드
		NavigableSet<String> rangeSet = treeSet.subSet("c", true, "f", true);
		for(String word : rangeSet) {
			System.out.println(word);
		}

	}
}

출력 화면

[c~f 사이의 단어 검색]
cherry
description
ever

 

 

 

 

참고 : [한빛미디어] 이것이 자바다 (신용권의 Java 프로그래밍 정복) Chapter 15.컬렉션 프레임워크
참고 : [도우출판] JAVA의 정석(3ND EDITION)-자바의 정석 최신 Java 8.0 포함 Chapter 11.컬렉션 프레임워크

'JAVA' 카테고리의 다른 글

인터페이스 (Interface)  (0) 2022.09.02
Map 컬렉션 (HashMap, TreeMap)  (0) 2022.08.28
List 컬렉션  (0) 2022.08.28
컬렉션 프레임워크  (0) 2022.08.28
람다식 (Lambda Expression)  (0) 2022.08.28

댓글