자바의 정석[기초편]으로 공부한 내용을 정리한 글입니다.
HashSet의 기본 개념 및 Set 계층구조 이해
HashSet의 성질
- 순서가 없음 (순서 X)
- 중복 불가 (중복 X)
- 즉, List와 반대로 "순서를 유지하지 않으면서 중복을 허용하지 않는" 컬렉션입니다.
계층 구조
- HashSet은 Set 인터페이스를 구현한 대표적인 클래스입니다.
- Set 인터페이스의 하위에는 SortedSet, 그 하위에는 TreeSet이 있고,
- TreeSet: 정렬된 상태로 저장되는 Set
- HashSet: 정렬도 안 되고 순서도 없음
순서를 유지하려면?
- HashSet은 순서 없음.
➡ LinkedHashSet을 사용하면 입력한 순서를 유지할 수 있음
TreeSet은 정렬 기반
- 값의 범위 검색 (from ~ to)이나 정렬된 데이터 유지가 필요할 때 유용
HashSet의 생성자와 주요 메서드
생성자 종류
- HashSet(): 기본 생성자
- HashSet(Collection c): 다른 컬렉션으로 초기화
- HashSet(int initialCapacity): 초기 용량 설정
- HashSet(int initialCapacity, float loadFactor): 초기 용량 + 로드 팩터 설정
- loadFactor: 얼마나 채워졌을 때 내부 배열을 확장할 것인가 (예: 0.8 → 80% 차면 확장)
주요 메서드
- 추가/삭제
- add(Object o) / addAll(Collection c)
- remove(Object o) / removeAll(Collection c)
- retainAll(Collection c) : 교집합 유지
- clear() : 전부 삭제
- 조회/상태
- isEmpty() : 비어 있는지
- size() : 저장된 요소 수
- contains(Object o) : 포함 여부
- containsAll(Collection c) : 전부 포함 여부
- 배열 변환
- toArray() : 배열로 변환
- toArray(Object[] a) : 지정 배열로 복사
- 반복자
- iterator() : Set의 요소를 하나씩 꺼낼 수 있는 Iterator 객체 반환
HashSet 사용 예제 – 중복 제거 & 출력
코드 설명
Object[] objArr = {"1", new Integer(1), "2", "2", "3", "3", "4", "4"};
Set set = new HashSet();
- "1"(문자열)과 new Integer(1)(정수 객체)은 서로 다르다고 간주됩니다.
- "2", "3", "4"는 문자열로서 중복되므로 한 번만 저장됩니다.
for(int i=0; i < objArr.length; i++) {
set.add(objArr[i]);
}
- 위 배열의 요소들을 하나씩 HashSet에 넣습니다.
- HashSet이므로 중복은 자동 제거됩니다.
System.out.println(set);
- HashSet 내부 요소를 직접 출력
- 하지만 HashSet은 순서를 보장하지 않기 때문에, 출력 순서는 랜덤입니다.
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
- 반복자를 이용해 요소들을 하나씩 꺼내서 출력
1
1
2
3
4
HashSet → 정렬이 필요할 때 (List로 변환)
Set set = new HashSet();
while(set.size() < 6) {
int num = (int)(Math.random()*45)+1;
set.add(num);
}
- 1~45 사이의 랜덤 숫자를 중복 없이 6개 저장
- 중복이 자동 제거되므로, 6개가 될 때까지 반복
List list = new LinkedList(set);
- HashSet → LinkedList로 복사
- 이제 list는 정렬 가능한 리스트
Collections.sort(list);
System.out.println(list);
- 리스트 정렬
- 중복 없는 6개의 랜덤 숫자 → 정렬된 상태로 출력
요약
HashSet | 순서 없음, 중복 제거, 빠른 검색 |
LinkedHashSet | 순서 유지 + 중복 제거 |
TreeSet | 자동 정렬 + 중복 제거 |
add() | 요소 추가 |
iterator() | 반복자 꺼내기 |
size() | 저장된 개수 |
Collections.sort() | List 정렬용 유틸리티 |
HashSet은 어떻게 중복을 판단하나요?
HashSet은 객체를 저장하기 전에 "이미 동일한 객체가 존재하는가?" 를 판단하여
- 없으면 저장
- 있으면 저장하지 않음
이 판단 기준이 바로 equals()와 hashCode() 메서드입니다.
HashSet에 저장되는 객체의 조건
equals()와 hashCode()를 왜 오버라이딩해야 하나요?
HashSet은 다음과 같이 동작합니다:
- 저장하려는 객체의 hashCode() 값을 먼저 계산합니다.
- 같은 해시코드가 있으면 → 그 객체들과 equals() 비교를 합니다.
- equals() 결과가 true면 → 중복된 객체로 판단하고 저장하지 않습니다.
따라서:
- 두 객체가 같은 객체인지 판별하려면
반드시 equals()와 hashCode()를 둘 다 오버라이딩해야 합니다.
예제
class Person {
String name;
int age;
public boolean equals(Object obj) {
if (!(obj instanceof Person)) return false;
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
public int hashCode() {
return Objects.hash(name, age);
}
}
이 코드에서 중요한 점:
- equals: 이름과 나이가 모두 같아야 같은 객체로 간주합니다.
- hashCode: Objects.hash(name, age) → name과 age가 같으면 동일한 hashCode를 반환합니다.
예시
HashSet set = new HashSet();
set.add("abc");
set.add("abc"); // 중복이라 저장 안됨
set.add(new Person("David", 10));
set.add(new Person("David", 10)); // 중복이라 저장 안됨
해설:
- "abc"는 문자열이므로 equals()와 hashCode()가 이미 정의되어 있어서 동작 OK.
- Person("David", 10)은 equals와 hashCode를 오버라이딩했기 때문에
→ 똑같은 이름과 나이의 Person은 중복으로 간주되어 두 번째 add는 무시됩니다.
만약 equals와 hashCode를 오버라이딩하지 않으면?
set.add(new Person("David", 10));
set.add(new Person("David", 10));
- 위 두 줄은 다른 객체로 간주되어 둘 다 저장됩니다.
- 이유: 기본 Object 클래스의 equals()는 주소(참조값) 비교를 하기 때문입니다.
→ 값은 같아도 다른 주소에 존재하므로 다르다고 판단합니다.
요약
HashSet | 순서 없음, 중복 불가 |
중복 판단 기준 | hashCode() → equals() |
직접 만든 클래스 객체 저장 시 | 반드시 equals()와 hashCode()를 오버라이딩해야 중복 판단 가능 |
오버라이딩 기준 | 비교 기준이 되는 필드(예: name, age)를 이용해서 구현 |
'Java' 카테고리의 다른 글
HashMap (0) | 2025.06.24 |
---|---|
TreeSet (0) | 2025.06.24 |
Comparator와 Comparable (0) | 2025.06.23 |
Arrays의 메서드 (0) | 2025.06.23 |
Iterator, ListIterator, Enumeration (0) | 2025.06.23 |