CS 정리

java Collection, Hash 연관 정리

문쿼리 2025. 4. 7. 18:08

1. java Collection framework 개념

Collection 인터페이스, 구현 클래스, 유틸리티 클래스, 정렬, iterator, 동기화, stream, spliterator, comparator/comparable 등을 포함하고 있음.

데이터를 효율적으로 저장/관리/검색/순회/정렬할 수 있게 해주는 인터페이스 + 구현체 + 유틸리티 클래스의 생태계 전체

 

2. Collection 개념

데이터의 집합을 다루기 위한 인터페이스 계층 구조, 여러 객체를 효율적으로 추가/삭제/검색 할 수 있도록 도와주는 인터페이스

List, Set, Queue 같은 "요소들의 집합"을 다루는 인터페이스 계층 구조

 

3. Collection 종류

Collection List, Set, Queue의 부모 인터페이스
List 순서가 있는 데이터 집합 (중복 허용)
Set 순서가 없고 중복을 허용하지 않음
Queue FIFO 구조 (먼저 넣은 게 먼저 나옴)
Map 키-값 쌍으로 저장 (Collection은 아님)

* 일반적으로 Collection을 학습할 때 Map을 함께 학습함.

 

4. Collection 조합

List + Set 중복 제거 후 순서 유지
List + Map 순서 유지하면서 빠른 조회
Set + Map 조건 필터링된 조회
List<Map<>> 복잡한 데이터 구조 (DB, JSON 등)
Queue + Set BFS, 이벤트 중복 방지

 

5. Hash 개념

특정 값을 고정된 크기의 정수값(hash code)으로 바꾸는 기능.

같은 입력 값이면 항상 동일한 해시값을 가지기 때문에 빠른 검색과 비교에 용이함.

빠른검색, 빠른 삽입/삭제, 키 기반 접근
HashMap, HashSet, ConcurrentHashMap, hashCode()

 

* 간혹 Hash를 정수값(hash code)이 아닌, 주소값이라고 표현하는 경우가 있는데 이는 틀린 표현.

특정 환경에서는 메모리 기반의 주소 값을 활용하는 사례가 있지만, java에서 hash는 항상 32bit 고정 크기 정수임

ex) "hash".hashCode() -> 3198781

s[0]*31ⁿ⁻¹ + s[1]*31ⁿ⁻² + ... + s[n-1]

 

6. Hash Collision 개념

동일한 hash code를 갖는 경우가 있음, 32bit 의 고정 정수(약 43억 개)의 경우의 수 보다 입력의 가능성의 수가 훨씬 많기 때문에 같은 해시코드를 가질 가능성이 있음.

ex) "Aa".hashCode() == "BB".hashCode() // 두 문자열의 hash code는 같다.

 

장애 발현이 흔하지 않은 이유?

java에서 내부적으로 충돌을 처리해주는 구조로 기능을 제공하고 있음.

같은 hash code를 가진 값을 비교하면, hash code 이후에 equals를 활용하고 있음.

(= hashCode() 또는 equals()를 잘못 구현하는 경우 장애로 이어질 수 있음)

 

1) 체이닝(LinkedList) 활용

해시 충돌 발생 시 같은 버킷에 들어온 값들을 연결해서 저장하는 방식.

HashSet, HashMap의 경우 같은 hash code가 저장된 경우, 동일한 인덱스에 여러 값들을 저장해두고, equals를 활용하여 정확한 값을 찾아내고 있다.

 

2) 트리(Red-Black Tree) 활용 Java 8이상

체이닝만 사용하는 경우 충돌이 잦을 때, 성능이 O(n)으로 낮아짐

동일 버킷에 8개 이상 발생하는 경우, 해당 버킷을 Red-Black Tree로 전환해서 탐색 성능을 O(log n)으로 향상 시킴

 

7. @Override hashCode(), equals() 

ORM( JPA ) 사용 시 혹은 HashMap, HashSet등 hash 인터페이스의 key로 dto 사용 시 필요함.

Class User {
  String name;
  
  User(String name) {
    this.name = name;
  }
}

User A = new User("A");
Set<User> userSet = new HashSet<>();
userSet.add(A);

userSet.contains(new User("A")); // false

 

* 직접 @Override 구현

public class User {
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User other = (User) o;
        return this.age == other.age && Objects.equals(this.name, other.name);
    }

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

User A = new User("A");
Set<User> userSet = new HashSet<>();
userSet.add(A);

userSet.contains(new User("A")); // true

 

* Lombok 사용

@EqualsAndHashCode
public class User {
    private String name;
    private int age;
}

@EqualsAndHashCode(of = {"name"}) // 부분 일치로 dto 가 일치하는지 체크할 수 있다
public class User {
    private String name;
    private int age;
}

 

* Record 사용(java 14이상, 불변 데이터의 경우 활용)

public record User(String name, int age) {
    // equals()와 hashCode()는 자동 생성됨
}

 

8. Comparable/ Comparator

구분 Comparable Comparator
어디에 정의? 객체 클래스 내부 클래스 외부 또는 람다식
인터페이스명 Comparable<T> Comparator<T>
메서드명 int compareTo(T o) int compare(T o1, T o2)
기본 정렬? 가능 기본은 아님, 원하는 만큼 만들 수 있음
사용 예 Collections.sort(list) list.sort(comparator), TreeSet<>(comparator)

* 간단하게 dto 내부 한 요소 등으로 정렬하는 경우 = Comparable, 여러 요소를 기준으로 정렬한다면 Comparator 활용