![[Java] Set , HashSet 살펴보기](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgeAwV%2FbtsrIbNphDP%2F18TFJBGtwdOzDQh5YFVUY0%2Fimg.png)

Set ?
- 집합을 다루는 자료구조
- 중복 x, 순서 x
- 대표적인 구현체 : HashSet / TreeSet / LinkedHashSet / EnumSet
- 내부적으로 Map 자료 구조에 Key에 데이터를 담는다 (Map은 Key의 중복 허용하지 않음)
//java.util.HashSet.class
private transient HashMap<E, Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
this.map = new HashMap();
}
//..
public boolean add(E e) {
return this.map.put(e, PRESENT) == null; // HashMap의 Key에 element를 담는다
}
HashSet
- 내부적으로 HashMap<E, Object> map 으로 자료 관리한다
- equals(), hashCode() 사용하여 객체의 동등성 확인
TreeSet
- NavigableMap<E, Object> m = new TreeMap() 으로 자료 관리한다
- 범위 검색과 정렬에 유리
- HashSet보다 데이터 추가/삭제 시간이 더 걸린다 (??, 트리 구조라서 재배치때문인가 싶음)
- 이진 탐색 트리(binary search tree)로 구현
LinkedHashSet
- 순서를 보장하는 HashSet
참고. 시간 복잡도
https://gyuwon95.tistory.com/99
[Java] 자바 컬렉션들의 시간 복잡도 (Big O)
Java Collection 인터페이스 특징 구현클래스 List 순서가 있는 데이터의 집합, 데이터의 중복을 허용 ArrayList, LinkedList, Stack, Vector Set 객체의 순서가 없으며, 데이터의 중복을 허용하지 않음 HashSet, Tree
gyuwon95.tistory.com
Interface Set
- java.util 패키지 위치
- Java Collection Framework
public interface Set<E> extends Collection<E> { // Collection Interface 상속
int size();
boolean isEmpty();
boolean contains(Object var1);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] var1);
boolean add(E var1);
boolean remove(Object var1);
boolean containsAll(Collection<?> var1);
boolean addAll(Collection<? extends E> var1);
boolean retainAll(Collection<?> var1);
boolean removeAll(Collection<?> var1);
void clear();
boolean equals(Object var1);
int hashCode();
//.. 이하 생략
}
HashSet<E> 살펴보기
- java.util 패키지 위치
- Set 인터페이스 구현체
- 내부적으로 HashMap<E, Object> map 에 자료를 저장하고 관리함
1) 데이터 추가
HashSet의 경우 내부적으로 HashMap 을 사용하고, Key에 element를 담는다
// java.util.HashSet
public boolean add(E e) {
return this.map.put(e, PRESENT) == null;
}
*.put(K key, V, value) 의 경우 키에 대한 값이 있으면 이전 값(=PRESENT)을 리턴하고, 없을 경우 null을 리턴한다
2) 데이터 삭제
- HashMap에 구현된 remove() 메서드 호출
- 값이 존재할 경우 이전 값을 리턴하고, 없을 경우 null 리턴 (HashSet의 값은 클래스 맴버 Object PRESENT로 고정)
public boolean remove(Object o) {
return this.map.remove(o) == PRESENT;
}
3) 데이터 확인
내부적으로 Map의 containsKey() 호출하여 존재 여부 확인
public boolean contains(Object o) {
return this.map.containsKey(o);
}
4) 교집합, 합집합, 차집합 (by 테스트 학습)
각 instance method 와 stream api 사용하는 경우를 살펴본다
초기화 선언
public class Test {
private Set<Integer> setA;
private Set<Integer> setB;
private static Set<Integer> setOf(Integer... values) {
return new HashSet<>(Arrays.asList(values));
}
@BeforeEach
void setUp() {
setA = setOf(1, 2, 3, 4);
setB = setOf(2, 4, 6, 8);
}
//.. 테스트 작성
}
Intersection (교집합)
*.retainAll() 의 경우 AbstractCollection.class에 메서드 호출한다
// java.util.AbstractCollection
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<E> it = this.iterator(); // this = 기준
while(it.hasNext()) {
if (!c.contains(it.next())) { // c에 포함되어 있지 않는 경우 this의 해당 요소 삭제
it.remove();
modified = true;
}
}
return modified;
}
예시
@DisplayName("두 집합의 교집합(Intersection)을 구한다")
@Test
void retainAll() {
//given
Set<Integer> givenData = setOf(2, 4);
//when
setA.retainAll(setB);
//then
assertThat(setA).hasSize(2).isEqualTo(givenData);
assertThat(setA).containsExactly(2, 4);
}
@DisplayName("stream filter 로 Set의 교집합을 구한다")
@Test
void intersectionByStream() {
//given
Set<Integer> givenData = setOf(2, 4);
//when
Set<Integer> result = setA.stream().filter(setB::contains).collect(Collectors.toSet());
//then
assertThat(result).isEqualTo(givenData);
}
Union (합집합)
*.addAll() 의 경우 AbstractCollection.class에 메서드 호출
// java.util.AbstractCollection
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
Iterator var3 = c.iterator();
while(var3.hasNext()) {
E e = var3.next();
if (this.add(e)) { // this에 c의 요소가 없는 경우 true 리턴
modified = true;
}
}
return modified;
}
// HashSet.class
public boolean add(E e) {
return this.map.put(e, PRESENT) == null; // 키 존재하면 이전 값, 없으면 null 리턴
}
예시
@DisplayName("두 집합의 합집합(Union)을 구한다")
@Test
void addAll() {
//given
Set<Integer> givenData = setOf(1, 2, 3, 4, 6, 8);
//when
setA.addAll(setB);
//then
assertThat(setA).hasSize(6).isEqualTo(givenData);
assertThat(setA).containsExactly(1, 2, 3, 4, 6, 8);
}
@DisplayName("Stream concat 으로 Set의 합집합을 구한다")
@Test
void unionByStream() {
//given
Set<Integer> givenData = setOf(1, 2, 3, 4, 6, 8);
//when
Set<Integer> result = Stream.concat(setA.stream(), setB.stream()).collect(Collectors.toSet());
//then
assertThat(result).hasSize(6).isEqualTo(givenData);
}
Relative Complement (차집합)
- *.removeAll() 의 경우 AbstractSet.class에 오버라이딩 메서드를 호출한다.
- AbstractCollection 클래스 상속받아 AbstractSet.class에서 오버라이딩한 것으로 확인
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator i;
Object e;
if (this.size() > c.size()) { // 사이즈 기준으로 분리
for(i = c.iterator(); i.hasNext(); modified |= this.remove(e)) { // this에서 e(c 컬렉션)를 지움
e = i.next();
}
} else {
i = this.iterator(); // this를 기준
while(i.hasNext()) {
if (c.contains(i.next())) { // c 콜렉션에 값이 있으면 remove
i.remove();
modified = true;
}
}
}
return modified;
}
예시
@DisplayName("집합 A 기준으로 집합 B에 대한 차집합을 구한다")
@Test
void removeAll() {
//given
Set<Integer> givenData = setOf(1, 3);
//when
setA.removeAll(setB);
//then
assertThat(setA).hasSize(2).isEqualTo(givenData);
assertThat(setA).containsExactly(1, 3);
}
@DisplayName("stream filter 로 Set의 차집합을 구한다")
@Test
void relativeComplementByStream() {
//given
Set<Integer> givenData = setOf(1, 3);
//when
Set<Integer> result = setA.stream().filter(a -> !setB.contains(a)).collect(Collectors.toSet());
//then
assertThat(result).hasSize(2).isEqualTo(givenData);
}
참고
https://www.baeldung.com/java-set-operations
'알고리즘 > 자료구조' 카테고리의 다른 글
[BOJ22252] 정보 상인 호석 (자료구조, 해쉬맵, 우선 순위 큐) (0) | 2023.09.16 |
---|---|
[BOJ21276] 계보 복원가 호석 (자료구조, 해시맵, 인접 리스트, 그래프) (0) | 2023.09.13 |
[BOJ10799] 쇠막대기 (스택, 자료구조) (0) | 2023.09.13 |
[Java] HashMap Method, 해싱 충돌 살펴보기 (0) | 2023.08.19 |
[Java] Array, ArrayList, LinkedList 비교 (0) | 2023.08.17 |

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!