※ 본문은 혼자 공부한 내용을 기록한 글입니다. 오개념이 있다면 댓글로 알려주세요!
Java의 Set 인터페이스는 중복된 요소를 저장하지 않는 컬렉션이며, Set 인터페이스를 구현한 대표적인 클래스로는 HashSet이 있다.
[ 1 ] HashSet
HashSet은 내부적으로 HashMap을 이용해서 만들었으며, 해시 테이블 관련 내용은 해당 게시물을 참고하자.
HashSet에 새로운 요소를 추가할 때에는 add() 나 addAll()을 사용하는데, 만약 set에 저장되어 있는 요소와 같은 요소를 저장하려고 시도하면 해당 메서드는 false를 반환하며 중복된 요소를 저장하지 않는다.
List 인터페이스와 달리 HashSet은 내부적으로 HashMap을 이용하고 있어 저장순서를 유지하지 않으므로 저장순서를 유지하려면 LinkedHashSet을 사용해야 한다.
- HashSet의 메서드
메서드 | 내용 |
HashSet() | HashSet 객체 생성 |
HashSet(Collection c) | 주어진 컬렉션을 포함하는 HashSet 객체 생성 |
boolean add(Object o) | set에 새로운 객체 o 저장 |
boolean addAll(Collection c) | 주어진 컬렉션에 저장된 모든 객체를 추가 (합집합) |
void clear() | 저장된 모든 객체 삭제 |
boolean contains(Object o) | o를 포함하고 있으면 true, 아니라면 false |
boolean containsAll(Collection c) | 주어진 컬렉션에 저장된 모든 객체를 포함하고 있는지의 여부 반환 |
boolean remove(Object o) | o를 set에서 삭제 (성공 시 true, 실패 시 false 반환) |
boolean removeAll(Collection c) | 주어진 컬렉션에 저장된 모든 객체와 동일한 요소를 set에서 삭제 (성공 시 true, 실패 시 false 반환) |
boolean retainAll(Collection c) | 주어진 컬렉션에 저장된 모든 객체와 동일한 요소만 남기고 나머지는 삭제 (성공 시 true, 실패 시 false 반환) |
사용자 정의 클래스의 인스턴스를 HashSet에 중복되지 않도록 저장하기 위해서는 추가적인 작업이 필요하다.
import java.util.*;
public class Main {
public static void main(String[] args) {
Set set = new HashSet();
set.add("123");
set.add("123");
set.add(new Person("John", 24));
set.add(new Person("John", 24));
System.out.println(set);
}
}
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
위의 예제를 실행하면 [123, John 24, John 24]라는 결과가 출력될 것이다. 즉, 두 개의 Person 인스턴스는 name과 age 값이 같음에도 다른 객체로 인식되어 set에 저장된 것이다.
HashSet의 add()는 새로운 요소가 기존에 저장된 요소와 같은 것인지 판별하기 위해 추가하려는 요소의 equals()와 hashCode()를 호출한다. 그러므로 사용자 정의 클래스의 인스턴스를 HashSet에 저장하려면 equals()와 hashCode()를 적절하게 오버라이딩 해야 한다.
package set.hashset;
import java.util.*;
public class Main {
public static void main(String[] args) {
Set set = new HashSet();
set.add("123");
set.add("123");
set.add(new Person("John", 24));
set.add(new Person("John", 24));
System.out.println(set);
}
}
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Person) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
return false;
}
@Override
public int hashCode() {
return (name + age).hashCode();
}
}
String 클래스에는 이미 hashCode()가 잘 구현되어 있기 때문에 String 클래스의 hashCode()를 활용하면 쉽게 구현할 수 있다.
@Override
public int hashCode() {
return Objects.hash(name, age);
}
JDK 1.8부터 추가된 Objects 클래스의 hash() 를 통해서도 hashCode() 를 구현할 수 있다.
hashCode()는 다음의 조건을 만족해야 한다.
- hashCode()는 동일한 객체에 대해 동일한 int 값을 반환해야 한다. 단, 모든 실행에서 동일한 int값을 반환할 필요는 없으며 실행 중에만 같은 int 값을 반환하면 된다.
- equals() 를 통해 true를 얻은 두 객체는 항상 동일한 hashCode() 값을 반환받아야 한다.
HashSet 외에도 저장된 요소의 정렬과 범위검색(range search)에 높은 성능을 보이는 TreeSet이 있다. TreeSet은 특정 요소보다 작은 subSet, 특정 요소보다 큰 subSet, 특정 범위(from ~ to)의 subSet을 반환받을 수 있는 장점이 있으므로 상황에 맞는 구현 클래스를 사용하도록 하자.
'Programming > Java' 카테고리의 다른 글
[Java] 그래프 표현하기 - 인접 리스트 (2) | 2023.04.27 |
---|---|
[Java] 스택(Stack)과, 큐(Queue) (3) | 2023.02.28 |
[Java] JVM의 메모리 구조(Runtime Data Area) (0) | 2023.02.22 |
[Java] 클래스, 객체, 인스턴스의 차이 및 객체 배열 (0) | 2023.02.17 |
[Java] JVM, Java 환경변수 설정(Mac, 기본 zsh 쉘) (5) | 2023.01.18 |