hanker

Java - Set 사용 법 (HashSet, TreeSet, LinkedHashSet) 본문

JAVA

Java - Set 사용 법 (HashSet, TreeSet, LinkedHashSet)

hanker 2025. 2. 15. 00:00
반응형

Java에서 Set이란 단순하게 얘기하면 중복을 제거해주는 컬렉션이다.

 

이번 글에서는 단순하게 중복을 제거하는 컬렉션 이상의 내용을 알아보자.


1. Set의 기본 사용법

 

1-1. Set이란?

Set은 중복된 값을 허용하지 않으며 순서가 보장되지 않는다.

 

 

1-2. HashSet, TreeSet, LinkedHashSet 사용 예제

public class Main {
    public static void main(String[] args) {
        // HashSet: 빠른 검색(O(1)), 순서 보장X
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Python");
        hashSet.add("Java");
        hashSet.add("C++");
        hashSet.add("Java");  // 중복된 값은 추가되지 않음
        System.out.println("HashSet: " + hashSet);

        // TreeSet: 요소들이 정렬된 상태로 저장됨 (오름차순)
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("Python");
        treeSet.add("Java");
        treeSet.add("C++");
        treeSet.add("Java");  // 중복된 값은 무시됨
        System.out.println("TreeSet: " + treeSet);

        // LinkedHashSet: 삽입 순서를 유지함
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("Python");
        linkedHashSet.add("Java");
        linkedHashSet.add("C++");
        linkedHashSet.add("Java");  // 중복된 값은 무시됨
        System.out.println("LinkedHashSet: " + linkedHashSet);
    }
}

- HashSet : 빠르게 검색이 가능하지만, 순서가 보장이 안된다.

- TreeSet : 요소들이 오름차순으로 정렬된 상태로 저장된다.

- LinkedHashSet : 요소들의 삽입 순서대로 유지된다.

 


2. 사용자 정의 객체와 중복 처리 (equals & hashCode)

 

Set에 사용자 정의 객체를 저장할 때는 동일성을 판단하기 위해 equals()hashCode() 메서드를 올바르게 오버라이드해야 한다.

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // equals와 hashCode를 오버라이드하여 이름과 나이가 같으면 같은 객체로 판단
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}

public class Main {
    public static void main(String[] args) {
        Set<Person> people = new HashSet<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Alice", 30));  // 중복된 객체

        System.out.println("사람 목록: " + people);
        // equals, hashCode를 올바르게 구현했으므로 중복이 제거되어 2개만 출력된다.
    }
}


3. 집합 연산 (교집합, 합집합, 차집합)

 

Set의 removeAll(), addAll(), removeAll() 메서드를 이용하여 교집합, 합집합, 차집합 등을 구할 수 있다.

import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<Integer> setA = new HashSet<>();
        Set<Integer> setB = new HashSet<>();

        // setA에 1, 2, 3, 4, 5 추가
        for (int i = 1; i <= 5; i++) {
            setA.add(i);
        }
        // setB에 4, 5, 6, 7 추가
        for (int i = 4; i <= 7; i++) {
            setB.add(i);
        }

        // 합집합 (Union)
        Set<Integer> union = new HashSet<>(setA);
        union.addAll(setB);
        System.out.println("합집합: " + union);  // [1, 2, 3, 4, 5, 6, 7]

        // 교집합 (Intersection)
        Set<Integer> intersection = new HashSet<>(setA);
        intersection.retainAll(setB);
        System.out.println("교집합: " + intersection);  // [4, 5]

        // 차집합 (A - B)
        Set<Integer> difference = new HashSet<>(setA);
        difference.removeAll(setB);
        System.out.println("차집합 (setA - setB): " + difference);  // [1, 2, 3]
    }
}

 


4. Iterator를 사용한 Set 순회

 

Set은 인덱스 기반 접근이 불가능하므로, Iterator를 사용하거나 향상된 for문을 통해 순회한다.

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<String> languages = new HashSet<>();
        languages.add("Java");
        languages.add("Python");
        languages.add("C++");

        // Iterator 사용
        Iterator<String> iter = languages.iterator();
        while (iter.hasNext()) {
            String lang = iter.next();
            System.out.println("Iterator: " + lang);
        }

        // 향상된 for문 사용
        for (String lang : languages) {
            System.out.println("향상된 for문: " + lang);
        }
    }
}


5. Stream API와 Set

 

Java 8 이상에서는 Stream API를 활용하여 Set의 요소들을 필터링, 변환, 집계할 수 있다.

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        Set<String> fruits = new HashSet<>();
        fruits.add("apple");
        fruits.add("banana");
        fruits.add("cherry");
        fruits.add("apple"); // 중복은 무시됨

        // 요소들을 대문자로 변환하여 List로 수집
        var upperFruits = fruits.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println("대문자 변환 결과: " + upperFruits);

        // "a"를 포함한 요소만 필터링
        var filtered = fruits.stream()
                .filter(fruit -> fruit.contains("a"))
                .collect(Collectors.toSet());
        System.out.println("\"a\"를 포함한 과일: " + filtered);
    }
}

 


정리

 

Java Set의 구현체별 차이, 사용자 정의 객체, 집합 연산, Iterator와 Stream API의 활용 등 다양한 활용 법에 대해서 알아봤다.

순서가 중요하지 않고 빠른 성능이 필요할 때는 HashSet, 정렬이 필요할 때에는 TreeSet을 사용하면 된다.

끝.

반응형