본문으로 건너뛰기
김신건의 로그

[Java] Vector

· 수정 · 📖 약 2분 · 768자/단어 #java #collection #data-structure #list #legacy #synchronized
Vector, java.util.Vector, Java Vector, 벡터, Vector class

정의

java.util.VectorArrayList 와 거의 동일한 동적 배열 기반 List 이지만, 모든 public 메서드가 synchronized 로 묶여 있다. Java 1.0 (1996) 부터 존재하는 레거시 컬렉션.

JDK 1.2 의 Collections Framework 도입 시 List 인터페이스에 retrofit 됐으며, 같은 인터페이스를 구현하는 ArrayList 가 등장했다. 새 코드에서는 거의 쓰지 않는다.

시각화

내부 구조

public class Vector<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    protected Object[] elementData;
    protected int elementCount;
    protected int capacityIncrement;   // 0 이면 두 배씩 증가 (ArrayList 와 다름)

    public synchronized boolean add(E e) { ... }
    public synchronized E get(int index) { ... }
    public synchronized E remove(int index) { ... }
    // ... 거의 모든 메서드에 synchronized
}

ArrayList 와의 두 가지 차이:

  1. capacityIncrement: 0 일 때 두 배씩 확장 (ArrayList 는 1.5 배).
  2. 모든 메서드가 synchronized: 메서드 호출마다 this 의 모니터 락을 획득.

주요 연산 비용

복잡도 자체는 ArrayList 와 동일. 다만 매 호출마다 락 acquire/release 가 더해진다.

메서드시간추가 비용
get(int i)O(1)락 acquire/release
add(E e)amortized O(1)락 acquire/release
add(int i, E e)O(n)락 acquire/release
remove(int i)O(n)락 acquire/release
size()O(1)락 acquire/release

단일 스레드 환경이라면 락은 무경합 (uncontended) 이라 비용이 매우 작지만, 그래도 ArrayList 대비 측정 가능한 오버헤드가 있다.

메서드별 동기화의 한계

각 메서드 호출은 atomic 하지만, 복합 연산 은 race condition 의 여지가 그대로 남는다.

Vector<Integer> v = new Vector<>();
// ...

// ❌ 각 메서드는 atomic 이지만 두 호출 사이에 다른 스레드가 끼어들 수 있다
if (!v.contains(42)) {
    v.add(42);   // 다른 스레드가 그 사이에 42 를 넣었을 수 있음
}

// ✅ 명시적 락으로 묶기
synchronized (v) {
    if (!v.contains(42)) {
        v.add(42);
    }
}

CAUTION

“Vector 가 thread-safe 하다” 는 오해다. 단일 메서드는 thread-safe 지만, 두 개 이상의 메서드를 묶는 일관성은 호출자가 따로 보장해야 한다. 진정한 동시성 컬렉션은 CopyOnWriteArrayList 또는 ConcurrentHashMap 같은 java.util.concurrent 의 컬렉션.

iterator 의 fail-fast

Vector 의 iterator 도 fail-fast iterator. 순회 도중 다른 스레드가 add/remove 하면 ConcurrentModificationException. 락으로 외부 동기화를 해야 안전한 순회가 가능하다.

synchronized (v) {
    for (Integer x : v) {
        process(x);  // 이 블록 안에서는 다른 스레드 진입 불가
    }
}

ArrayList vs Vector

항목ArrayListVector
동기화✓ (메서드 단위)
도입 시점JDK 1.2 (1998)JDK 1.0 (1996)
용량 증가1.5 배2 배 (또는 capacityIncrement)
메서드 호출 비용작음+ 락 비용
복합 연산 안전성외부 동기화 필요여전히 외부 동기화 필요
새 코드 권장

왜 새 코드에서 쓰지 않는가

  1. 단일 스레드 라면 락이 불필요한데도 비용을 낸다.
  2. 다중 스레드 라도 메서드별 락만으로는 복합 연산이 안전하지 않다. 결국 외부 동기화가 필요하다.
  3. 진짜 동시성 컬렉션 (CopyOnWriteArrayList, ConcurrentLinkedQueue 등) 이 훨씬 우수하다.
  4. 레거시 잔재, JDK 가 retrofit 했으나 이상적인 컬렉션 설계는 아니다.

그러면 언제 만나게 되나

  • 레거시 코드 유지보수: 90 년대 후반 ~ 2000 년대 초 코드
  • Stack<E> 사용 코드: Stack 이 Vector 를 상속함. 새 코드에선 Deque<E> = new ArrayDeque<>() 권장
  • AWT/Swing API: JTable, JList 의 일부 모델이 Vector 사용

대안

의도대안
단일 스레드 ListArrayList
외부 동기화 ListCollections.synchronizedList(new ArrayList<>())
읽기 多, 쓰기 거의 없음CopyOnWriteArrayList
동시 큐/덱ConcurrentLinkedDeque, LinkedBlockingDeque
스택Deque<E> = new ArrayDeque<>()

참고

이 글의 용어 (11개)
[Java] ArrayListjava
정의 는 동적 배열 (dynamic array) 로 구현된 . 내부적으로 를 들고 다니며, 용량이 부족해지면 새 배열로 옮긴다. Java 의 가장 흔한 컬렉션 구현체. 인덱스 기…
[Java] Collectionjava
정의 는 그룹으로 묶인 객체들을 표현하는 최상위 인터페이스. JCF (Java Collections Framework) 의 입구이자, / / / 모두 이를 확장한다. 자체는 직접…
[Java] ConcurrentHashMapjava
정의 는 고동시성 환경에서 사용 가능한 구현. JSR-166 (Java 5) 도입, Java 8 에서 내부 구조가 크게 재작성됐다. 과 같은 인터페이스를 제공하면서 thread-…
[Java] ConcurrentModificationExceptionjava
정의 (CME) 는 가 컬렉션의 구조 변경을 탐지했을 때 던지는 unchecked 예외 ( ). 이름과 달리 반드시 동시성 (멀티스레드) 문제일 때만 발생하는 것이 아니다. 단일…
[Java] CopyOnWriteArrayListjava
정의 는 쓰기 시 배열 전체를 복사 하는 thread-safe 구현. 읽기에는 lock 이 전혀 없고, 쓰기에는 으로 직렬화한다. (JSR-166) 의 컬렉션. 읽기 압도적 다,…
[Java] Fail-Fast Iteratorjava
정의 Fail-Fast Iterator 는 순회 도중 컬렉션의 구조가 변경되면 즉시 을 던지는 iterator. , , 등 대부분의 컬렉션의 iterator 가 이 방식. 목적은…
[Java] Iterablejava
정의 는 루프로 순회 가능한 모든 타입의 최상위 인터페이스. 단 하나의 추상 메서드, 를 정의한다. 인터페이스가 을 extends 하므로 , , , 등 모든 컬렉션이 자동으로 대…
[Java] Listjava
정의 는 순서가 있고 중복이 허용 되는 컬렉션을 표현하는 인터페이스. 각 원소는 0 부터 시작하는 정수 인덱스로 접근 가능. 인터페이스를 확장하며, 의 모든 성질을 물려받는다. …
[Java] Objectjava
정의 는 Java 의 모든 클래스의 최상위 부모 (root) 클래스. 가 명시되지 않은 클래스는 컴파일러가 자동으로 를 붙인다. 인터페이스는 클래스가 아니라 를 직접 상속하지는 …
[Java] ReentrantLockjava
정의 는 키워드와 같은 상호 배제 (mutual exclusion) 를 제공하는 클래스 기반 락. JSR-166 (Java 5) 에서 추가됐다. "재진입 (reentrant)" …
Blocking (블로킹)concurrency
정의 Blocking 은 호출이 결과가 준비될 때까지 호출 스레드를 멈춰두는 실행 방식. 멈춰 있는 동안 그 스레드는 다른 일을 할 수 없다. OS 가 스레드를 sleep 상태로…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기