[알고리즘] 19. 해시셋(HashSet)
지난 글에서는 해시맵(HashMap)에 대해 정리했습니다. 이번 글에서는 알고리즘 문제에서 중복 제거와 빠른 존재 확인에 자주 사용하는 해시셋(HashSet)에 대해 알아보겠습니다.
HashSet은 값을 중복 없이 저장하는 자료구조입니다. 배열이나 리스트처럼 순서대로 값을 저장하는 것보다, 어떤 값이 이미 있는지 빠르게 확인하는 데 더 적합합니다.
HashSet은 중복을 허용하지 않고, 값의 존재 여부를 빠르게 확인할 수 있는 자료구조입니다.
1. HashSet이란?
HashSet은 여러 값을 저장할 수 있는 자료구조입니다. 하지만 일반 리스트와 다르게 중복된 값을 저장하지 않습니다.
예를 들어 숫자 1, 2, 2, 3, 3, 3을 HashSet에 넣으면 실제로는 1, 2, 3만 남습니다.
입력 값: 1, 2, 2, 3, 3, 3
HashSet에 저장된 값: 1, 2, 3
즉 HashSet은 같은 값이 여러 번 들어와도 하나만 저장합니다.
HashSet의 핵심은 중복 없는 값 저장입니다.
2. HashSet이 필요한 상황
알고리즘 문제에서는 어떤 값이 이미 나왔는지 빠르게 확인해야 하는 경우가 많습니다. 이럴 때 HashSet을 사용하면 좋습니다.
- 중복된 값을 제거해야 할 때
- 어떤 값이 이미 등장했는지 확인해야 할 때
- 서로 다른 값의 개수를 구해야 할 때
- 두 배열에 공통으로 들어 있는 값을 찾아야 할 때
- 방문 여부를 빠르게 체크해야 할 때
- 값의 범위가 커서 카운팅 배열을 쓰기 어려울 때
특히 존재 여부 확인이 중요한 문제에서는 HashSet을 먼저 떠올리면 좋습니다.
3. Java에서 HashSet 사용하기
Java에서 HashSet을 사용하려면 먼저 import가 필요합니다.
import java.util.HashSet;
기본 사용 형태는 다음과 같습니다.
HashSet<Integer> set = new HashSet<>();
위 코드는 Integer 값을 저장할 수 있는 HashSet을 만든 것입니다. 문자열을 저장하고 싶다면 String 타입을 사용하면 됩니다.
HashSet<String> set = new HashSet<>();
4. add()로 값 추가하기
HashSet에 값을 넣을 때는 add()를 사용합니다.
import java.util.HashSet;
HashSet<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
System.out.println(set);
출력 결과는 순서가 다를 수 있지만, 내용은 다음과 비슷합니다.
[20, 10, 30]
HashSet은 저장 순서를 보장하지 않습니다. 따라서 넣은 순서와 출력 순서가 다를 수 있습니다.
HashSet은 순서보다 중복 제거와 빠른 검색이 중요한 자료구조입니다.
5. 중복 값을 넣으면 어떻게 될까요?
HashSet은 중복을 허용하지 않습니다. 이미 들어 있는 값을 다시 add()해도 같은 값이 하나 더 생기지 않습니다.
HashSet<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(10);
set.add(20);
set.add(30);
System.out.println(set);
System.out.println(set.size());
출력 결과는 순서가 다를 수 있지만, 크기는 3입니다.
[20, 10, 30]
3
10과 20을 두 번 넣었지만 HashSet에는 각각 하나씩만 저장됩니다.
HashSet에 같은 값은 한 번만 저장됩니다.
6. contains()로 값 존재 확인하기
HashSet에서 특정 값이 있는지 확인할 때는 contains()를 사용합니다.
HashSet<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
System.out.println(set.contains(20));
System.out.println(set.contains(40));
출력 결과는 다음과 같습니다.
true
false
20은 set 안에 있으므로 true, 40은 없으므로 false가 출력됩니다.
알고리즘 문제에서 특정 값이 이미 등장했는지 확인할 때 contains()를 자주 사용합니다.
7. remove()로 값 삭제하기
HashSet에서 값을 삭제할 때는 remove()를 사용합니다.
HashSet<String> set = new HashSet<>();
set.add("java");
set.add("spring");
set.add("sql");
set.remove("spring");
System.out.println(set);
출력 결과는 순서가 다를 수 있지만, spring이 제거된 것을 확인할 수 있습니다.
[java, sql]
존재하지 않는 값을 remove()해도 보통 오류가 발생하지 않습니다. 다만 실제로 삭제되었는지 여부가 필요하다면 반환값을 확인할 수 있습니다.
boolean removed = set.remove("python");
System.out.println(removed);
python이 set에 없었다면 false가 출력됩니다.
8. size()와 isEmpty()
HashSet에 저장된 값의 개수를 확인할 때는 size()를 사용합니다. 비어 있는지 확인할 때는 isEmpty()를 사용합니다.
HashSet<Integer> set = new HashSet<>();
System.out.println(set.isEmpty());
set.add(1);
set.add(2);
set.add(2);
set.add(3);
System.out.println(set.size());
System.out.println(set.isEmpty());
출력 결과는 다음과 같습니다.
true
3
false
2를 두 번 넣었지만 중복은 저장되지 않으므로 크기는 3입니다.
9. 서로 다른 값의 개수 구하기
HashSet을 사용하면 배열 안에 서로 다른 값이 몇 개인지 쉽게 구할 수 있습니다.
import java.util.HashSet;
int[] numbers = {1, 2, 2, 3, 3, 3, 4};
HashSet<Integer> set = new HashSet<>();
for (int num : numbers) {
set.add(num);
}
System.out.println(set.size());
출력 결과는 다음과 같습니다.
4
배열에는 1, 2, 3, 4 네 종류의 숫자가 있습니다. HashSet은 중복을 제거하기 때문에 서로 다른 값의 개수를 구할 때 매우 편리합니다.
서로 다른 값의 개수는 HashSet에 넣은 뒤 size()를 확인하면 됩니다.
10. 중복 여부 확인하기
배열에 중복 값이 있는지 확인하는 문제도 HashSet으로 쉽게 풀 수 있습니다.
값을 하나씩 set에 넣기 전에 이미 존재하는지 확인하면 됩니다.
import java.util.HashSet;
int[] numbers = {1, 3, 5, 3, 7};
HashSet<Integer> set = new HashSet<>();
boolean hasDuplicate = false;
for (int num : numbers) {
if (set.contains(num)) {
hasDuplicate = true;
break;
}
set.add(num);
}
System.out.println(hasDuplicate);
출력 결과는 다음과 같습니다.
true
숫자 3이 두 번 등장했기 때문에 중복이 있다고 판단할 수 있습니다.
11. 두 배열의 공통 값 찾기
HashSet은 두 배열에 공통으로 들어 있는 값을 찾을 때도 유용합니다.
먼저 한 배열의 값을 HashSet에 저장한 뒤, 다른 배열의 값을 하나씩 확인하면 됩니다.
import java.util.HashSet;
int[] a = {1, 2, 3, 4};
int[] b = {3, 4, 5, 6};
HashSet<Integer> set = new HashSet<>();
for (int num : a) {
set.add(num);
}
for (int num : b) {
if (set.contains(num)) {
System.out.println("공통 값: " + num);
}
}
출력 결과는 다음과 같습니다.
공통 값: 3
공통 값: 4
배열 a에 있는 값을 set에 저장해두었기 때문에, 배열 b의 값이 공통으로 존재하는지 빠르게 확인할 수 있습니다.
12. 문자열 중복 제거하기
HashSet은 문자열 중복 제거에도 사용할 수 있습니다. 예를 들어 단어 목록에서 중복 단어를 제거해보겠습니다.
import java.util.HashSet;
String[] words = {"java", "spring", "java", "sql", "spring"};
HashSet<String> set = new HashSet<>();
for (String word : words) {
set.add(word);
}
System.out.println(set);
출력 결과는 순서가 다를 수 있지만, 중복이 제거된 단어만 남습니다.
[spring, java, sql]
같은 문자열이 여러 번 등장해도 HashSet에는 하나만 저장됩니다.
13. HashSet 순회하기
HashSet에 저장된 값을 하나씩 확인하려면 향상된 for문을 사용할 수 있습니다.
HashSet<String> set = new HashSet<>();
set.add("java");
set.add("spring");
set.add("sql");
for (String value : set) {
System.out.println(value);
}
출력 순서는 보장되지 않습니다.
spring
java
sql
HashSet은 순서가 중요한 자료구조가 아닙니다. 순서가 필요하다면 리스트로 변환해서 정렬하거나, 다른 자료구조를 고려해야 합니다.
HashSet은 저장 순서와 출력 순서를 보장하지 않습니다.
14. HashSet을 List로 변환하기
HashSet에서 중복을 제거한 뒤 정렬이 필요하다면 List로 변환할 수 있습니다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
int[] numbers = {3, 1, 2, 3, 2, 1};
HashSet<Integer> set = new HashSet<>();
for (int num : numbers) {
set.add(num);
}
ArrayList<Integer> list = new ArrayList<>(set);
Collections.sort(list);
System.out.println(list);
출력 결과는 다음과 같습니다.
[1, 2, 3]
HashSet으로 중복을 제거한 뒤, ArrayList로 바꾸고 정렬하면 중복 없는 정렬 결과를 만들 수 있습니다.
15. HashMap과 HashSet의 차이
HashMap과 HashSet은 이름이 비슷하지만 사용 목적이 다릅니다.
| 구분 | HashMap | HashSet |
|---|---|---|
| 저장 형태 | key와 value | value만 저장 |
| 주요 목적 | key에 연결된 값 저장 | 중복 없는 값 저장 |
| 예시 | 단어 → 등장 횟수 | 등장한 단어 목록 |
| 대표 사용 | 빈도수 계산 | 중복 제거, 존재 확인 |
값마다 연결된 정보가 필요하면 HashMap을 사용하고, 값이 있는지만 확인하거나 중복을 제거하면 된다면 HashSet을 사용하면 됩니다.
HashMap은 key-value 저장, HashSet은 중복 없는 값 저장입니다.
16. HashSet의 시간복잡도
HashSet은 값을 빠르게 추가하고 확인할 수 있습니다. 알고리즘 문제에서는 주요 연산을 평균적으로 O(1)에 가깝게 생각하는 경우가 많습니다.
| 연산 | 평균 시간복잡도 |
|---|---|
| add() | O(1) |
| contains() | O(1) |
| remove() | O(1) |
| size() | O(1) |
입력 N개를 한 번 순회하면서 HashSet에 넣는다면 전체 시간복잡도는 보통 O(N)입니다.
for (int num : numbers) {
set.add(num);
}
이 코드는 numbers 배열의 모든 값을 한 번씩 확인하므로 O(N)입니다.
17. HashSet에서 자주 하는 실수
HashSet을 사용할 때는 아래 실수를 조심해야 합니다.
- HashSet이 입력 순서를 유지한다고 생각하는 경우
- 중복 값이 여러 번 저장된다고 착각하는 경우
- 값의 등장 횟수가 필요한데 HashSet을 사용하는 경우
- 정렬된 결과가 필요한데 HashSet 출력 결과를 그대로 사용하는 경우
- 객체를 저장할 때 중복 기준을 제대로 정의하지 않는 경우
- contains()를 사용해야 할 상황에서 반복문으로 매번 전체 탐색하는 경우
특히 등장 횟수가 필요한 문제에서는 HashSet이 아니라 HashMap을 사용해야 합니다.
중복 제거, 존재 확인 → HashSet
등장 횟수 계산 → HashMap
HashSet은 몇 번 나왔는지가 아니라, 나왔는지 여부를 확인하는 데 적합합니다.
18. 정리
이번 글에서는 HashSet에 대해 정리했습니다.
- HashSet은 중복을 허용하지 않는 자료구조이다.
- add()로 값을 추가하고, contains()로 존재 여부를 확인한다.
- remove()로 값을 삭제할 수 있다.
- HashSet은 저장 순서를 보장하지 않는다.
- 서로 다른 값의 개수는 HashSet에 넣은 뒤 size()로 확인할 수 있다.
- 중복 여부 확인, 공통 값 찾기, 문자열 중복 제거 등에 자주 사용된다.
- 등장 횟수가 필요하면 HashSet이 아니라 HashMap을 사용하는 것이 좋다.
- HashSet의 주요 연산은 평균적으로 O(1)에 가깝게 동작한다.
HashSet은 알고리즘 문제에서 매우 실용적인 자료구조입니다. 문제를 보고 “중복을 없애야 한다”, “이미 나온 값인지 확인해야 한다”는 생각이 들면 HashSet을 먼저 고려해볼 수 있습니다.
HashSet의 핵심은 중복 없이 저장하고, 값이 있는지 빠르게 확인하는 것입니다.
다음 글 예고
다음 글에서는 우선순위 큐(PriorityQueue)에 대해 알아보겠습니다.
일반 큐와의 차이, 우선순위가 높은 값부터 꺼내는 구조, 최소 힙과 최대 힙, Java PriorityQueue 사용법까지 예제로 정리해보겠습니다.

'Problem Solving > Algorithm' 카테고리의 다른 글
| [알고리즘] 21. 힙(Heap) (0) | 2026.07.04 |
|---|---|
| [알고리즘] 20. 우선순위 큐(PriorityQueue) (0) | 2026.07.03 |
| [알고리즘] 18. 해시맵(HashMap) (1) | 2026.07.01 |
| [알고리즘] 17. 덱(Deque) (0) | 2026.06.30 |
| [알고리즘] 16. 큐(Queue) (0) | 2026.06.29 |