[Java/자바] HashSet 클래스 사용법

HashSet

HashSet은 Set인터페이스를 구현한 가장 대표적인 컬렉션으로, 중복된 요소를 저장하지 않습니다.

그렇기 때문에 HashSet에 이미 저장되어 있는 요소를 추가하고자 한다면

객체를 저장하기 전에, 먼저 객체의 hashCode()메소드를 호출해서 해시 코드를 얻어낸 다음 저장되어 있는 객체들의 해시 코드와 비교한 뒤 

같은 해시 코드가 있다면 다시 equals() 메소드로 두 객체를 비교해서 true가 나오면 동일한 객체로 판단하고 중복 저장을 하지 않습니다.

 

ArrayList와 달리 저장순서를 유지하지 않기 때문에, 저장순서를 유지하고자 한다면 LinkedHashSet을 사용해야합니다.

 

 

HashSet 선언
import java.util.HashSet;

HashSet<타입> set = new HashSet<>(); // HashSet 객체 생성
HashSet<타입> set = new HashSet<>(int initialCapacity); // 주어진 값을 초기용량으로 하는 HashSet 객체 생성
HashSet<타입> set = new HashSet<>(Collection c); // 주어진 컬렉션을 포함하는 HashSet 객체 생성

 

HashSet 메서드
메서드 반환 타입 설 명
add(Object o) boolean 새로운 객체를 저장
addAll(Collection c) boolean 주어진 컬렉션에 저장된 모든 객체를 추가
clear() void 저장된 모든 객체를 삭제
contains(Object o) boolean 지정된 객체를 포함하는지 확인
containsAll(Collection c) boolean 주어진 컬렉션에 저장된 모든 객체들을 포함하는지 확인
isEmpty() boolean HashSet이 비어있는지 확인
iterator Iterator Iterator를 반환
remove(Object o) boolean 지정된 객체를 HashSet에서 삭제
removeAll(Collection c) boolean 주어진 컬렉션에 저장된 모든 객체와 동일한 것들을 HashSet에서 삭제 (차집합)
retainAll(Collection c) boolean 주어진 컬렉션에 저장된 객체와 동일한 것만 남기고 삭제 (교집합)
size() int 저장된 객체의 개수 반환
toArray() Object[] 저장된 객체들을 객체배열의 형태로 반환
toArray(Object[] a) Object[] 저장된 객체들을 주어진 객체배열(a)에 담는다.

 


중복값은 저장되지 않는 다는 것을 확인할 수 있는 예제

import java.util.*;

class HashSetEx1 {
	public static void main(String[] args) {
		Object[] objArr = {"1",new Integer(1),"2","2","3","3","4","4","4"};
		Set set = new HashSet();

		for(int i=0; i < objArr.length; i++) {
			set.add(objArr[i]);	// HashSet에 objArr의 요소들을 저장한다.
		}
		System.out.println("objArr =" + Arrays.toString(objArr));
		
             // HashSet에 저장된 요소들을 출력한다.
		System.out.println("해쉬맵 =" + set);	
	}
}

여기서 1이 두번 출력되었는데, 하나는 String 인스턴스고, 다른 하나는 Integer인스턴스이기 때문에 서로 다른 객체로 판별해 중복으로 간주하지 않았습니다.

 


HashSet을 이용한 로또 번호 뽑기

import java.util.*;

class HashSetLotto {
	public static void main(String[] args) {
		Set set = new HashSet();
		
		for (int i = 0; set.size() < 6 ; i++) {
			int num = (int)(Math.random()*45) + 1;
			set.add(new Integer(num));
		}
		
		// Collections.sort(set);  set은 정렬이 되지 않는다.

		//Collections 클래스의 sort를 사용하려면 List 인터페이스 타입을 필요로 한다.
		List list = new LinkedList(set);	// LinkedList(Collection c)
		Collections.sort(list);				// Collections.sort(List list)
		System.out.println(list);
	}
}

이 예제는 중복값은 저장되지 않는 HashSet의 특징을 이용해 로또번호를 만들었습니다.

주의 깊게 볼 것은, 정렬을 사용하는데 HashSet에 저장된 요소들을 LinkedList에 담았는데

Collections의 sort() 메서드는 인자로 List 인터페이스 타입을 필요로 하기 때문입니다.

 


 빙고게임 예제

import java.util.*; 

class Bingo { 
      public static void main(String[] args) { 
            Set set = new HashSet(); 
//          Set set = new LinkedHashSet(); 
            int[][] board = new int[5][5]; 

            for(int i=0; set.size() < 25; i++) { 
                  set.add((int)(Math.random()*50)+1+""); 
            } 

            Iterator it = set.iterator(); 

            for(int i=0; i < board.length; i++) { 
                  for(int j=0; j < board[i].length; j++) { 
                        board[i][j] = Integer.parseInt((String)it.next());
                        System.out.print((board[i][j] < 10 ? "  " : " ") + board[i][j]); 
                  } 
                  System.out.println(); 
            } 
      } 
}

위 예시에서 주의 깊게 봐야할 점은 2가지입니다.

 

첫 번째는 next()는 반환값이 Object 타입이라는 점입니다.

그래서 int 값을 얻기 위해 Object 타입을 String으로 형변환 후, Integer.parseInt로 int 타입으로 변환 하였습니다.

 

두 번째는 HashSet과 LinkedHashSet입니다.

HashSet으로 이 예제를 실행할 시 순서를 보장하지 않기 때문에 자체적인 저장방식에 따라 순서가 결정되어 같은 숫자가 비슷한 위치에 나오게 됩니다.

이 경우 LinkedHashSet을 사용하는 것이 더 좋은 선택이 됩니다.

 


두 인스턴스의 같은 값 중복 제거 예제

import java.util.*;

class HashSetEx3 {
	public static void main(String[] args) {
		HashSet set = new HashSet();

		set.add("안녕하세요");
		set.add("안녕하세요");
		set.add(new Person("H",20));
		set.add(new Person("H",20));

		System.out.println(set);
	}
}

class Person {
	String name;
	int age;

	Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String toString() {
		return name +":"+ age;
	}
}

 

이 예제를 실행하면 다음과 같이 "안녕하세요"는 중복되어 1번만 출력되었지만 "H:20"은 중복되었음에도 2번 출력되었습니다.

그 이유는 두 인스턴스의 값이 같음에도 불구하고 서로 다른 것으로 인식하여 2번 출력하게 되었는데

두 인스턴스를 같은 것으로 인식하게 하려면 아래처럼 hashCode() 메서드와 eqauls() 메서드를 오버라이딩 해야합니다.

import java.util.*;

class HashSetEx4 {
	public static void main(String[] args) {
		HashSet set = new HashSet();

		set.add(new String("안녕하세요"));
		set.add(new String("안녕하세요"));
		set.add(new Person2("H",20));
		set.add(new Person2("H",20));

		System.out.println(set);
	}
}

class Person2 {
	String name;
	int age;

	Person2(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public boolean equals(Object o) {
		if(o instanceof Person2) {
			Person2 tmp = (Person2)o;
			return name.equals(tmp.name) && age==tmp.age;
		}

		return false;
	}

	public int hashCode() {
		return (name+age).hashCode();
	}

	public String toString() {
		return name +":"+ age;
	}
}

이렇게 오버라이딩 해야하는 이유는 처음에 설명했듯이

HashSet의 add메서드는 새로운 요소를 추가하기 전에 기존에 저장된 요소와 같은 것인지 판별하기 위해

추가하려하는 요소의 equals()와 hashCode()를 호출하기 때문입니다.

그래서 목적에 맞게 출력하고자 한다면 equals()와 hashCode() 메서드들을 목적에 맞게 오버라이딩해야 합니다.

 


차집합, 교집합, 합집합 구하기 예제

import java.util.*;

class HashSetEx5 {
	public static void main(String args[]) {
		HashSet setA    = new HashSet();
		HashSet setB    = new HashSet();
		HashSet setHab = new HashSet();
		HashSet setKyo = new HashSet();
		HashSet setCha = new HashSet();

		setA.add("1");		setA.add("2");
		setA.add("3");		setA.add("4");
		setA.add("5");
		System.out.println("A = "+setA);

		setB.add("4");		setB.add("5");
		setB.add("6");		setB.add("7");
		setB.add("8");
		System.out.println("B = "+setB);

		Iterator it = setB.iterator();
		while(it.hasNext()) {
			Object tmp = it.next();
			if(setA.contains(tmp))
				setKyo.add(tmp);
		}

		it = setA.iterator();
		while(it.hasNext()) {
			Object tmp = it.next();
			if(!setB.contains(tmp))
				setCha.add(tmp);
		}

		it = setA.iterator();
		while(it.hasNext())
			setHab.add(it.next());

		it = setB.iterator();
		while(it.hasNext())
			setHab.add(it.next());

		System.out.println("A ∩ B = "+setKyo);
		System.out.println("A ∪ B = "+setHab);
		System.out.println("A - B = "+setCha); 
	}
}

 


참고자료 : 자바의 정석3