Java Set 완벽 가이드

유방현·2024년 10월 22일

1. Set 개요

Set은 Java 컬렉션 프레임워크의 한 종류로, 중복을 허용하지 않는 데이터 구조입니다.
주요 특징:

  • 중복 요소 불허
  • 순서 보장 여부는 구현체에 따라 다름
  • null 요소 허용 (구현체에 따라 다름)

2. Set 구현체 종류

HashSet

  • 해시 테이블에 요소 저장
  • 순서 보장하지 않음
  • O(1) 시간복잡도로 빠른 검색/삽입/삭제
  • null 허용

TreeSet

  • 이진 검색 트리(Red-Black Tree) 기반
  • 요소들이 정렬된 상태로 저장
  • 검색/삽입/삭제에 O(log n) 시간복잡도
  • null 불허

LinkedHashSet

  • HashSet + LinkedList
  • 삽입 순서 보장
  • O(1) 시간복잡도
  • null 허용

3. 시간복잡도

HashSet

  • 추가/삭제/검색: O(1)
  • next(): O(h/n) (h: bucket 크기, n: 데이터 수)
    • n이 적을 때: 대부분의 bucket이 비어있어 탐색 필요
    • n이 증가할 때: O(1)에 수렴

TreeSet

  • 추가/삭제/검색: O(log n)
  • 정렬된 순회: O(n)

4. 주요 집합 연산

// 집합 생성
Set<String> set1 = new HashSet<>(Arrays.asList("A", "B", "C"));
Set<String> set2 = new HashSet<>(Arrays.asList("B", "C", "D"));

// 합집합 (Union)
set1.addAll(set2);  // set1에 set2의 모든 요소 추가

// 차집합 (Difference)
set1.removeAll(set2);  // set1에서 set2에 있는 요소 제거

// 교집합 (Intersection)
set1.retainAll(set2);  // set1에서 set2와 공통된 요소만 유지

5. List의 중복 요소 제거하기

방법 1: Set 활용

// Set 변환 후 다시 List로 변환
List<String> list = Arrays.asList("A", "B", "C", "A", "B", "C");
Set<String> set = new HashSet<>(list);
List<String> uniqueList = new ArrayList<>(set);

방법 2: Stream API 활용

List<String> list = Arrays.asList("A", "B", "C", "A", "B", "C");
List<String> uniqueList = list.stream()
                             .distinct()
                             .collect(Collectors.toList());

6. List로 Set 연산 구현하기

// 합집합
public static <T> List<T> union(List<T> list1, List<T> list2) {
    List<T> union = new LinkedList<>();
    union.addAll(list1);
    for (T t : list2) {
        if (!union.contains(t)) union.add(t);
    }
    return union;
}

// 차집합
public static <T> List<T> difference(List<T> list1, List<T> list2) {
    List<T> difference = new LinkedList<>();
    difference.addAll(list1);
    for (T t : list2) {
        difference.remove(t);
    }
    return difference;
}

// 교집합
public static <T> List<T> intersection(List<T> list1, List<T> list2) {
    List<T> intersection = new LinkedList<>();
    for (T t : list1) {
        if (list2.contains(t)) intersection.add(t);
    }
    return intersection;
}

7. 실무 Best Practices

  1. 기본적으로 HashSet 사용 (O(1) 성능)
  2. 정렬이 필요한 경우 TreeSet 고려
  3. 삽입 순서 유지가 필요한 경우 LinkedHashSet 사용
  4. 멀티스레드 환경에서는 Collections.synchronizedSet() 사용
  5. 불변 Set이 필요한 경우 Collections.unmodifiableSet() 사용

8. 주의사항

  1. Set의 equals()와 hashCode() 메서드는 반드시 올바르게 구현
  2. TreeSet 사용 시 요소는 Comparable을 구현하거나 Comparator 제공
  3. 반복문에서의 요소 제거는 Iterator 사용
  4. null 저장 시 구현체별 제약사항 확인

0개의 댓글