Java 기초 (컬렉션 - Set(HashSet)

최병현·2026년 1월 10일

Java

목록 보기
33/38

이 내용은 Backend / Java(OOP & Collection Framework) 영역에서 “중복 제거”가 필요할 때 사용하는 핵심 자료구조다. Set은 순서(인덱스)가 없고, 중복을 허용하지 않으며, 주로 빠른 포함 여부 체크에 강하다.


1) Set(HashSet) 핵심 특징

  • No index: List처럼 get(0) 이런 접근이 없다.
  • No duplicates: 같은 값을 add() 해도 1개만 유지된다.
  • Order not guaranteed: 출력 순서는 입력 순서랑 다를 수 있다(HashSet).
  • Fast lookup: 내부적으로 hash 기반이라 평균적으로 contains가 빠르다.

2) HashSet<String> 중복 제거 예시

문자열은 이미 equals/hashCode가 잘 정의돼 있어서 중복 제거가 바로 된다. 아래 코드에서 "java"를 3번 add해도 실제로는 1개만 남는다.

HashSet<String> set = new HashSet<>();

set.add("java");
set.add("java");
set.add("java");

set.add("python");
set.add("1");
set.add("2");

System.out.println(set); // 순서는 보장 X, java는 1개만 존재

3) Iterator로 Set 순회하기

Set은 인덱스가 없으니까 보통 Iterator 또는 enhanced for로 순회한다. Iterator는 hasNext()로 다음 요소 존재 여부를 확인하고, next()로 요소를 꺼낸다.

Iterator<String> ir = set.iterator();

while (ir.hasNext()) {
    System.out.println(ir.next());
}

enhanced for가 더 깔끔하다.

for (String s : set) {
    System.out.println(s);
}

4) HashSet<Student>가 “중복 제거”를 못 하는 이유 (핵심)

여기서부터가 Set의 진짜 핵심이다. HashSet이 중복을 판단하는 기준은 딱 2단계다.

  • 1) hashCode()가 같은지 확인
  • 2) hashCode가 같으면 equals()로 “진짜 같은 객체인지” 확인

그런데 지금 Student 클래스는 Lombok의 @Data를 쓰고 있어서 equals/hashCode가 자동 생성되긴 한다. 문제는 “어떤 필드를 기준으로 같은 학생인지”가 명확하지 않으면 중복 제거가 의도대로 안 될 수 있다는 점이다.

실무에서는 보통 학생을 구분하는 고유값이 studentCode 같은 ID라면, equals/hashCode 기준도 그 ID로 고정하는 방식이 정석이다.


5) Student DTO 구조

현재 구조는 자동으로 studentCode가 증가하면서 만들어진다. 즉 이름이 같아도 studentCode가 다르면 다른 학생으로 봐야 한다.

import java.util.List;
import lombok.Data;

@Data
public class Student {

    public static int autoCode = 20260000;

    private int studentCode;
    private String name;
    private List<String> hobbys;

    public Student(String name) {
        studentCode = ++autoCode;
        this.name = name;
    }
}

6) HashSet<Student> + 취미(baseball) 가진 학생 찾기

여기 로직은 Set을 “중복 제거 용도”가 아니라 “학생 묶음 저장 + 빠른 순회/검색” 용도로 쓰는 예시다.

  • 학생들을 HashSet에 넣는다
  • Iterator로 전부 순회한다
  • 각 학생의 hobbys(List)에서 contains("baseball")이면 출력한다
HashSet<Student> studentSet = new HashSet<>();

Student student1 = new Student("gildong");
ArrayList<String> arrayList1 = new ArrayList<>();
arrayList1.add("baseball");
arrayList1.add("basketball");
arrayList1.add("golf");
student1.setHobbys(arrayList1);
studentSet.add(student1);

Student student2 = new Student("gilseo");
ArrayList<String> arrayList2 = new ArrayList<>();
arrayList2.add("soccer");
arrayList2.add("swim");
arrayList2.add("running");
student2.setHobbys(arrayList2);
studentSet.add(student2);

Student student3 = new Student("gilnam");
ArrayList<String> arrayList3 = new ArrayList<>();
arrayList3.add("running");
arrayList3.add("swim");
arrayList3.add("baseball");
student3.setHobbys(arrayList3);
studentSet.add(student3);

Student student4 = new Student("gilbook");
ArrayList<String> arrayList4 = new ArrayList<>();
arrayList4.add("soccer");
arrayList4.add("basketball");
arrayList4.add("baseball");
student4.setHobbys(arrayList4);
studentSet.add(student4);

Iterator<Student> iterator = studentSet.iterator();

while (iterator.hasNext()) {
    Student student = iterator.next();
    if (student.getHobbys().contains("baseball")) {
        System.out.println(student.getName());
    }
}

7) (중요) HashSet에 객체를 넣을 때

HashSet의 “중복 제거”를 진짜로 활용하려면, equals/hashCode 기준을 명확히 잡아야 한다. 학생이라면 보통 studentCode가 기준이 된다.

Lombok @Data는 모든 필드를 기준으로 equals/hashCode를 만들 수 있어서, 원하는 기준이 아닐 수도 있다. 그래서 아래처럼 “포함할 필드만” 지정하는 방식이 실무에서 더 안전하다.

import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.EqualsAndHashCode;

@Getter
@Setter
@ToString
@EqualsAndHashCode(of = "studentCode") // only studentCode for equality
public class Student {

    public static int autoCode = 20260000;

    private int studentCode;
    private String name;
    private List<String> hobbys;

    public Student(String name) {
        studentCode = ++autoCode;
        this.name = name;
    }
}

이렇게 하면 HashSet이 studentCode 기준으로 중복을 제거한다.


정리

  • Set은 “중복 제거”가 핵심, HashSet은 “순서 보장 없음”
  • String은 equals/hashCode가 이미 구현돼 있어 중복 제거가 바로 된다
  • 객체(Student)를 Set에 넣을 때는 equals/hashCode 기준이 가장 중요
  • Iterator는 Set 순회의 표준 방식(또는 enhanced for)
// 핵심 한 줄 요약:
// HashSet duplicates rule = (same hashCode) && (equals() == true)
profile
No Pain No Gain

0개의 댓글