Java HashSet 사용법
HashSet은 Set 인터페이스를 구현한 대표적인 컬렉션이다. java.util.HashSet 클래스로 제공되며, 내부적으로 HashMap을 사용해 원소를 관리한다.
HashSet의 핵심 특징
- 같은 원소의 중복 저장을 허용하지 않는다.
- 원소의 저장 순서를 보장하지 않는다.
- 인덱스가 없으므로
get()메서드를 제공하지 않는다. - 원소를 추가할 때는
add(), 포함 여부를 확인할 때는contains()를 사용한다.
HashSet 생성
Set<Integer> set1 = new HashSet<>(); // 빈 HashSet 생성
Set<Integer> set2 = new HashSet<>(set1); // set1의 원소를 복사
Set<Integer> set3 = new HashSet<>(10); // 초기 용량 지정생성자에 전달하는 숫자는 저장 가능한 최대 원소 수가 아니라 초기 용량이다.
값 추가
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(1);HashSet은 중복을 허용하지 않으므로 같은 값을 다시 추가해도 하나만 유지된다.
add()는 새로운 값이 추가되면 true, 이미 존재하는 값이면 false를 반환한다.
boolean added = set.add(1);값 삭제
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.remove(1); // 값 1 제거
set.clear(); // 모든 값 제거remove(value)는 지정한 값을 제거하고, clear()는 저장된 모든 값을 제거한다.
clear()는 컬렉션 객체를 계속 사용하면서 원소만 비울 때 사용한다. 사용이 끝난 모든 HashSet에 반드시 호출해야 하는 메서드는 아니다.
순회와 출력
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
System.out.println(set);HashSet을 출력하면 원소가 대괄호로 묶여 표시되지만 출력 순서는 보장되지 않는다.
반복자를 사용해 순회할 수 있다.
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}향상된 for 문을 사용할 수도 있다.
for (int number : set) {
System.out.println(number);
}값 검색
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
System.out.println(set.contains(1)); // true
System.out.println(set.contains(5)); // falsecontains(value)는 지정한 값이 존재하면 true, 존재하지 않으면 false를 반환한다.
시간복잡도
| 연산 | 평균 시간복잡도 |
|---|---|
add() | O(1) |
contains() | O(1) |
remove() | O(1) |
| 전체 순회 | O(N + capacity) |
평균 시간복잡도는 해시값이 고르게 분산된다는 전제다. 해시 충돌이 집중되면 조회·추가·삭제 성능이 O(N)까지 저하될 수 있다. 전체 순회 비용은 저장된 원소 수 N뿐 아니라 내부 버킷의 용량에도 영향을 받는다.
사용자 정의 객체를 저장할 때
HashSet은 hashCode()와 equals()를 사용해 중복 여부를 판단한다. 사용자 정의 객체를 저장할 때 두 메서드를 일관되게 구현하지 않으면 논리적으로 같은 객체도 서로 다른 원소로 저장될 수 있다.
또한 원소를 저장한 뒤 동등성 판단에 사용되는 필드를 변경하면 해당 원소를 contains()나 remove()로 찾지 못할 수 있다. 가능하면 불변 객체를 원소로 사용한다.
집합 연산
addAll(), retainAll(), removeAll()을 사용해 합집합, 교집합, 차집합을 구할 수 있다. 원본을 보존하려면 새로운 HashSet에 복사한 뒤 연산한다.
Set<Integer> a = new HashSet<>(Arrays.asList(1, 2, 3));
Set<Integer> b = new HashSet<>(Arrays.asList(3, 4, 5));
Set<Integer> union = new HashSet<>(a);
union.addAll(b); // 합집합: [1, 2, 3, 4, 5]
Set<Integer> intersection = new HashSet<>(a);
intersection.retainAll(b); // 교집합: [3]
Set<Integer> difference = new HashSet<>(a);
difference.removeAll(b); // 차집합: [1, 2]결과의 출력 순서는 보장되지 않는다.