Вопрос 7 · Раздел 4

Что такое Vector и чем он отличается от ArrayList?

Lock Coarsening — оптимизация JVM, которая объединяет несколько последовательных synchronized-блоков в один, чтобы снизить накладные расходы на блокировку.

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

Vector — устаревшая версия ArrayList, которая была в Java с самого начала.

Главная разница:

  Vector ArrayList
Синхронизация ✅ Все методы синхронизированы ❌ Нет
Скорость Медленнее Быстрее
Ресайз × 2 × 1.5
Когда использовать Только в legacy-коде, новый код — ArrayList или CopyOnWriteArrayList  

Пример:

// ❌ Не используйте Vector
Vector<String> v = new Vector<>();  // Медленный!

// ✅ Используйте ArrayList
List<String> list = new ArrayList<>();

Почему Vector медленный:

  • Каждый метод = synchronized
  • Даже если работаете в одном потоке!

🟡 Middle Level

Технические отличия

// Vector:
public synchronized boolean add(E e) {  // ← synchronized!
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

// ArrayList:
public boolean add(E e) {  // ← Нет synchronized!
    modCount++;
    add(e, elementData, size);
    return true;
}

Резайз

Vector: capacity × 2
  10 → 20 → 40 → 80
  → Быстро растёт, больше wasted memory

ArrayList: capacity × 1.5
  10 → 15 → 22 → 33
  → Экономнее, меньше фрагментация

Почему синхронизация Vector НЕ помогает

// ❌ Vector НЕ потокобезопасен для составных операций!
if (!vector.contains(obj)) {  // Проверка
    vector.add(obj);           // ← Другой поток может вклиниться!
}

Это называется race condition **«check-then-act»**: проверка (`contains`) и действие (`add`)  две отдельные операции, между которыми другой поток может вмешаться.

// Всё равно нужен внешний synchronized:
synchronized (vector) {
    if (!vector.contains(obj)) {
        vector.add(obj);
    }
}

Альтернативы

// Без синхронизации:
ArrayList<String> list = new ArrayList<>();

// С синхронизацией (много чтений):
List<String> list = new CopyOnWriteArrayList<>();

// С синхронизацией (универсальный):
List<String> list = Collections.synchronizedList(new ArrayList<>());

// Для очередей:
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();

Как выбрать:

  • Один поток → ArrayList
  • Много потоков, много чтений → CopyOnWriteArrayList
  • Много потоков, смешанная нагрузка → Collections.synchronizedList
  • Много потоков, очередь → ConcurrentLinkedQueue

🔴 Senior Level

Lock Contention

100 потоков вызывают vector.get(0):
  → Все ждут один монитор
  → Сериализация → 0 параллелизма!
  
ArrayList:
  → 100 потоков читают одновременно
  → Lock-free reading

JIT Lock Coarsening

Lock Coarsening — оптимизация JVM, которая объединяет несколько последовательных synchronized-блоков в один, чтобы снизить накладные расходы на блокировку.

JVM пытается оптимизировать:
  vector.get(0);
  vector.get(1);
  vector.get(2);
  
→ Lock Coarsening: один lock на все три
→ Но это маскирует проблему, не решает!

LSP Violation: Stack наследуется от Vector

LSP (Liskov Substitution Principle) — принцип ООП: подкласс должен быть заменяем базовым классом без изменения корректности программы. Stack нарушает это, потому что добавляет методы, несовместимые с семантикой стека.

// Stack extends Vector!
Stack<String> stack = new Stack<>();
stack.push("A");
stack.add(0, "B");  // ← Нарушает семантику стека!

// → Stack может делать всё, что Vector
// → Это нарушение Liskov Substitution Principle

Enumeration vs Iterator

// Vector имеет Enumeration (устаревший)
Enumeration<String> e = vector.elements();
while (e.hasMoreElements()) {
    e.nextElement();  // Не fail-fast!
}

// Iterator (современный)
Iterator<String> it = vector.iterator();
// → Fail-fast, как у ArrayList

Production Experience

Реальный сценарий: Vector убил throughput

  • Приложение: 1000 RPS, Vector для логов
  • Lock contention: 80% времени на synchronized
  • Решение: ArrayList
  • Результат: +400% throughput

Best Practices

  1. НЕ используйте Vector — это legacy
  2. ArrayList — по умолчанию
  3. CopyOnWriteArrayList — для thread-safe
  4. Collections.synchronizedList — универсальный
  5. ConcurrentLinkedQueue — для очередей
  6. Stack → ArrayDeque — для стеков
  7. Enumeration → Iterator — современный

Резюме для Senior

  • Vector = legacy, synchronized, медленный
  • ArrayList = быстрее, без синхронизации
  • Синхронизация Vector не даёт реальной thread-safety
  • Lock Contention убивает параллелизм
  • Ресайз × 2 → больше фрагментация
  • Stack extends Vector = LSP violation
  • CopyOnWriteArrayList = правильная thread-safe альтернатива
  • Никогда не используйте Vector в новом коде

🎯 Шпаргалка для интервью

Обязательно знать:

  • Vector = legacy, все методы synchronized → медленный даже в одном потоке
  • Резайз: Vector × 2 (больше фрагментация) vs ArrayList × 1.5 (экономнее)
  • Синхронизация Vector НЕ даёт реальной thread-safety для составных операций (race condition «check-then-act»)
  • Lock Contention: 100 потоков вызывают vector.get() → сериализация → 0 параллелизма
  • Stack extends Vector = LSP violation — можно вызвать get(index), нарушая LIFO-семантику
  • Enumeration (Vector) устарел → используйте Iterator (fail-fast)
  • Альтернативы: ArrayList (один поток), CopyOnWriteArrayList (много чтений), Collections.synchronizedList (универсальный)
  • Реальный кейс: замена Vector → ArrayList дала +400% throughput

Частые уточняющие вопросы:

  • Почему synchronized в Vector не гарантирует thread-safety? — Составные операции (check-then-act) всё равно требуют внешнего synchronized
  • Что такое LSP violation в контексте Stack? — Stack наследуется от Vector, можно вызвать add(0,e) — нарушает LIFO
  • Когда JIT Lock Coarsening маскирует проблему? — Объединяет последовательные synchronized-блоки, но это не решает lock contention
  • Чем CopyOnWriteArrayList лучше Vector? — Копия при записи, lock-free чтение, лучше для сценариев «много чтений, мало записей»

Красные флаги (НЕ говорить):

  • ❌ “Vector — потокобезопасная замена ArrayList” — synchronized не даёт реальной thread-safety для составных операций
  • ❌ “Vector × 2 ресайз лучше чем ArrayList × 1.5” — наоборот, больше фрагментация и wasted memory
  • ❌ “Используйте Stack как стек” — Stack extends Vector, используйте ArrayDeque
  • ❌ “Enumeration лучше Iterator” — Enumeration устарел, не fail-fast

Связанные темы:

  • [[Что такое Stack]]
  • [[В чём разница между ArrayList и LinkedList]]
  • [[Какие основные интерфейсы Collection Framework]]
  • [[Что такое Deque]]