1. 컬렉션 프레임워크

image.png

왜 Map은 Collection Interface 를 구현하지 않는가?

Collection 은 단일 요소들의 집합을 다룬다 라는 것을 명시하는 인터페이스 반면, Map 은 key-value 쌍을 이룬다.

예를들어, Collection 의 add(E e) 메서드는 단일 요소를 추가하는데 Map 은 put(K key, V value)로 두 개의 요소를 필요로 한다. Map에서 이를 구현한다면 add(Entry<K,V>) 형태로 구현할 수 있는데 직관적이지 않고 타입 안정성도 떨어진다. 또 다른 예시로, contains(Object o)는 요소의 존재를 확인하지만, Map은 ContainsKey()와 containsValue() 로 key 와 value 를 명시하여 확인해야 한다.

이러한 개념적 차이 때문에 별도의 계층 구조를 갖는다.

2. List Interface

동적 배열의 구현체

내부적으로 Object elementData 배열을 사용한다. 기본 생성자로 생성하면 초기 용량은 10이며, 요소가 추가될 때마다 용량을 체크하고 만약 용량을 초과하면 1.5배 증가시키는 식으로 동작한다.

transient 로 직렬화를 최적화 하는 것이 핵심. ArrayList 는 직렬화 시 실제 size만큼 저장하여 효율성을 높인다.

// ArrayList 내부 구조 (단순화)
public class ArrayList<E> {
    private Object[] elementData;
    private int size;
    private static final int DEFAULT_CAPACITY = 10;
    
    // 용량 증가 로직
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5배 증가
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}

왜 1.5배 또는 2배를 증가시키는가?

시간 복잡도와 메모리 효율성의 균형을 맞추기 위함.

배열 크기의 증가량이 너무 작다면 (예: +1) 빈번하게 배열을 복사해야 함. 너무 크다면 메모리가 낭비된다.

1.5~2배는 O(1) 추가 연산을 보장하면서 메모리 효율성을 유지하기 위함.