컬렉션 인터페이스와 구현 선택
배열은 길이가 고정되어 있지만 컬렉션은 원소 추가와 제거, 검색을 위한 다양한 계약을 제공합니다.
List, Set, Queue, Map은 같은 데이터를 담는 그릇 이름이 아니라 순서, 중복, 접근 방식의 차이를 나타냅니다.
변수 타입은 가능한 한 인터페이스로 선언하고 생성할 때 필요한 성능과 순서 보장을 가진 구현을 고릅니다.
컬렉션은 기본 타입 대신 래퍼 객체를 저장합니다.
이 절에서는 연산 요구에서 자료구조를 선택하는 순서를 세웁니다.
구현을 고를 때는 “무엇이 가장 빠른가”보다 어떤 연산이 자주 일어나고 어떤 결과 순서를 약속해야 하는지부터 적습니다. 평균 시간 복잡도가 좋아도 입력 규모가 작거나 순서 변환이 반복되면 더 복잡한 구현이 오히려 손해일 수 있습니다. 인터페이스는 의미를, 구현은 비용과 부가 보장을 담습니다.
List는 순서와 중복을 보존한다
위치 기반 접근과 같은 값 여러 개가 필요할 때 사용합니다.
중간 삽입이 많은지 임의 접근이 많은지에 따라 구현을 비교합니다.
Set은 원소의 유일성을 계약으로 둔다
contains와 중복 제거가 핵심이며 equals와 hashCode 계약이 결과에 직접 영향을 줍니다.
CollectionChoices.java는 같은 문자열을 List의 사건 순서, Set의 유일성, Queue의 처리 순서로 각각 해석합니다.
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
public class CollectionChoices {
public static void main(String[] args) {
List<String> ordered = new ArrayList<>();
ordered.add("A"); ordered.add("A");
Set<String> unique = new HashSet<>(ordered);
Queue<String> pending = new ArrayDeque<>(List.of("first", "second"));
System.out.println(ordered);
System.out.println(unique.size());
System.out.println(pending.remove());
System.out.println(pending.peek());
}
}같은 두 문자열을 넣어도 List는 사건 두 건으로 보존하고 Set은 값 종류 하나로 축약합니다. Queue의 remove는 맨 앞 값을 소비한 반면 peek는 다음 값을 제거하지 않고 확인합니다.
[A, A]
1
first
second같은 입력도 List에서는 중복 두 개, Set에서는 하나, Queue에서는 처리 순서로 관찰됩니다.
Queue와 Deque는 처리 순서를 표현한다
앞에서 꺼내고 뒤에 넣는 작업, 양끝 작업을 메서드 이름으로 드러냅니다.
인덱스로 처리 순서를 흉내 내지 않습니다.
아래 선언은 구현 이름보다 호출 코드가 기대하는 연산을 앞에 드러냅니다. 나중에 구현을 바꾸더라도 인터페이스에 없는 우연한 메서드에 의존하지 않게 됩니다.
List<String> names = new ArrayList<>(); // 순서·중복·인덱스
Set<String> ids = new HashSet<>(); // 유일성·포함 여부
java.util.Deque<String> work = new ArrayDeque<>(); // 양끝 처리
java.util.Map<String, Integer> scores = new java.util.HashMap<>(); // 키 조회// 구현보다 먼저 인터페이스 타입으로 요구를 드러낸다.구현 타입은 필요한 보장으로 고른다
ArrayList, HashSet, ArrayDeque가 흔한 기본이지만 정렬, 삽입 순서, 동시성이 필요하면 다른 구현을 선택합니다.
pending.remove()를 poll()로 바꾼 뒤 빈 큐에서 한 번 더 호출해 보세요. remove는 NoSuchElementException, poll은 null로 빈 상태를 표현합니다. 컬렉션 종류뿐 아니라 실패를 예외로 볼지 값 없음으로 볼지도 API 선택의 일부입니다.
동시 접근이 생긴다고 기존 컬렉션을 무조건 synchronized 래퍼로 감싸지는 않습니다. 필요한 연산이 단일 조회인지 여러 단계의 원자적 갱신인지 확인하고, 그 계약을 직접 제공하는 concurrent 컬렉션을 뒤에서 선택해야 합니다.
컬렉션 선택은 원소 타입보다 먼저 순서, 중복, 핵심 연산, 동시성이라는 요구를 표로 적어 보면 명확해집니다.