코드 한 줄의 기록

Java HashSet, TreeSet, LinkedHashSet 완벽 정리 - 특성과 정렬 방법 본문

JAVA

Java HashSet, TreeSet, LinkedHashSet 완벽 정리 - 특성과 정렬 방법

CodeByJin 2025. 10. 26. 08:52
반응형

자바로 개발하다 보면 중복 없이 데이터를 관리해야 하는 경우가 정말 많습니다. 이럴 때 Set 컬렉션을 사용하는데, 막상 HashSet, TreeSet, LinkedHashSet 중에서 어떤 것을 선택해야 할지 고민될 때가 있죠. 오늘은 제가 공부한 내용을 정리하면서, 여러분께도 각 Set의 특성과 정렬 방법을 공유해보려고 합니다.

Set 인터페이스의 기본 특징

먼저 Set이 무엇인지부터 정리하고 넘어가겠습니다. Set은 Collection 인터페이스를 상속받은 인터페이스로, 중복을 허용하지 않는다는 것이 가장 큰 특징입니다. List처럼 같은 값을 여러 번 저장할 수 없고, 한 번만 저장됩니다.

  • 중복된 값을 저장할 수 없음
  • 기본적으로 순서를 보장하지 않음 (일부 구현체 제외)
  • 인덱스로 접근 불가능
  • null 값도 한 번만 저장 가능

HashSet - 가장 빠른 성능의 기본 Set

HashSet은 Set 인터페이스의 가장 기본적인 구현체입니다. 실무에서 가장 많이 사용하는 Set이기도 하죠.

HashSet의 내부 구조

private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();

public boolean add(E e) {
    return map.put(e, PRESENT) == null;
}

HashMap의 Key에 저장하려는 데이터를 넣고, Value에는 PRESENT라는 더미 객체를 넣습니다. HashMap은 Key의 중복을 허용하지 않으니까, 자연스럽게 중복 제거가 되는 원리입니다.

HashSet의 특징

장점

  • 검색, 삽입, 삭제 연산이 평균 O(1) 시간 복잡도를 가짐
  • 세 가지 Set 중에서 가장 빠른 성능
  • 대용량 데이터 처리에 적합

단점

  • 저장 순서를 전혀 보장하지 않음
  • 정렬된 상태로 유지되지 않음

중복 체크 원리

HashSet이 중복을 판단하는 원리는 hashCode()equals()를 이용하는 것입니다.

public class Member {
    private String name;
    private int age;
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Member) {
            Member m = (Member) obj;
            return name.equals(m.name) && age == m.age;
        }
        return false;
    }
}

HashSet 사용 예시

Set<String> fruits = new HashSet<>();
fruits.add("사과");
fruits.add("바나나");
fruits.add("오렌지");
fruits.add("사과");

System.out.println(fruits);

TreeSet - 자동 정렬되는 Set

TreeSet은 정렬이 필요한 상황에서 유용합니다. 순위 시스템이나 범위 검색이 필요할 때 자주 사용됩니다.

TreeSet의 내부 구조

TreeSet은 Red-Black Tree로 구현되어 있습니다. 내부적으로 TreeMap을 활용합니다.

private transient NavigableMap<E,Object> m;

public TreeSet() {
    this(new TreeMap<E,Object>());
}

TreeSet의 특징

  • 저장 시 자동 오름차순 정렬
  • Comparator로 정렬 기준 변경 가능
  • 범위 검색 메서드 지원 (subSet(), headSet())
  • O(log n) 복잡도

TreeSet 사용 예시

TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(30);
numbers.add(10);
numbers.add(20);
numbers.add(50);
System.out.println(numbers);

Comparator 활용

TreeSet<Integer> desc = new TreeSet<>(Collections.reverseOrder());
desc.add(10);
desc.add(20);
desc.add(30);
System.out.println(desc);

LinkedHashSet - 입력 순서를 기억하는 Set

LinkedHashSet은 HashSet과 거의 같지만, 삽입 순서를 기억합니다.

LinkedHashSet 사용 예시

Set<String> linkedSet = new LinkedHashSet<>();
linkedSet.add("첫번째");
linkedSet.add("두번째");
linkedSet.add("세번째");
System.out.println(linkedSet);

LinkedHashSet 특성

  • 입력 순서 보장
  • HashSet보다 약간 느림
  • 평균 O(1) 성능

세 가지 Set 비교

특징 HashSet LinkedHashSet TreeSet
내부 구조 HashMap LinkedHashMap TreeMap (Red-Black Tree)
순서 보장 X O (삽입 순서) O (정렬 순서)
시간 복잡도 O(1) O(1) O(log n)
정렬 X 입력순만 자동정렬

Set 정렬 방법

1. TreeSet으로 변환

TreeSet<Integer> sorted = new TreeSet<>(hashSet);
System.out.println(sorted);

 

2. List로 변환 + Collections.sort()

List<String> list = new ArrayList<>(fruits);
Collections.sort(list);

 

3. Stream API 활용

List<Integer> sortedList = numbers.stream()
    .sorted()
    .collect(Collectors.toList());

 

HashSet : 빠르고 단순한 중복 제거용

LinkedHashSet : 순서 보존 + 중복 제거

TreeSet : 정렬이 필요한 경우

 

정렬이 필요하다면 TreeSet, Collections.sort(), Stream.sorted() 중에서 선택하면 됩니다. 실무에서는 대부분 HashSet을 사용하고, 필요할 때만 변환하여 정렬하는 방식이 효율적입니다.

 

코드를 작성하다가 중복 제거와 정렬이 동시에 필요할 때, 이 글이 도움이 되길 바랍니다!

반응형