
이 내용은 Backend / Java(OOP & Collection Framework) 영역에서 “중복 제거”가 필요할 때 사용하는 핵심 자료구조다. Set은 순서(인덱스)가 없고, 중복을 허용하지 않으며, 주로 빠른 포함 여부 체크에 강하다.
문자열은 이미 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개만 존재
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);
}
여기서부터가 Set의 진짜 핵심이다. HashSet이 중복을 판단하는 기준은 딱 2단계다.
그런데 지금 Student 클래스는 Lombok의 @Data를 쓰고 있어서 equals/hashCode가 자동 생성되긴 한다. 문제는 “어떤 필드를 기준으로 같은 학생인지”가 명확하지 않으면 중복 제거가 의도대로 안 될 수 있다는 점이다.
실무에서는 보통 학생을 구분하는 고유값이 studentCode 같은 ID라면, equals/hashCode 기준도 그 ID로 고정하는 방식이 정석이다.
현재 구조는 자동으로 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;
}
}
여기 로직은 Set을 “중복 제거 용도”가 아니라 “학생 묶음 저장 + 빠른 순회/검색” 용도로 쓰는 예시다.
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());
}
}
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 기준으로 중복을 제거한다.
// 핵심 한 줄 요약:
// HashSet duplicates rule = (same hashCode) && (equals() == true)