← Назад к вопросам

Какая сложность получения элемента в середине в LinkedList?

1.0 Junior🔥 251 комментариев
#Коллекции#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Какая сложность получения элемента в середине в LinkedList?

Прямой ответ: O(n)

Получение элемента по индексу (get) в LinkedList имеет сложность O(n) — линейную, где n — количество элементов в списке.

Почему O(n)?

LinkedList — это связный список, не массив:

ArrayList (массив):     LinkedList (связный список):
index:  0  1  2  3      node: A  B  C  D
value: 10 20 30 40      value: 10 20 30 40
                        links: A->B->C->D

get(2) = O(1)           get(2) = O(n) нужно пройти
(прямой доступ по       через A, B, C
индексу в памяти)

Анализ операции get(int index)

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;  // node() — O(n)
}

private Node<E> node(int index) {
    // Оптимизация: если index < size/2, ищем с начала
    // иначе ищем с конца
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++) {
            x = x.next;  // Проход по ссылкам
        }
        return x;
    } else {
        Node<E> x = last;
        for (int i = size; i > index; i--) {
            x = x.prev;  // Проход в обратном направлении
        }
        return x;
    }
}

Практический пример

LinkedList<Integer> list = new LinkedList<>();
for (int i = 0; i < 1000; i++) {
    list.add(i);
}

// Получение элемента в конце
int element = list.get(999);  // O(n/2) - проход от конца

// Получение элемента в начале
int element = list.get(0);    // O(1) - вершина

// Получение элемента в середине
int element = list.get(500);  // O(n/2) - проход половину

Сравнение LinkedList vs ArrayList

ОперацияLinkedListArrayList
get(index)O(n)O(1) ✓
add(E)O(1) ✓O(1) amortized
add(int, E)O(n)O(n)
remove(index)O(n)O(n)

❌ Антипаттерн: Итерация по LinkedList через get()

// МЕДЛЕННО: O(n²)
for (int i = 0; i < linkedList.size(); i++) {
    int value = linkedList.get(i);  // get() каждый раз O(n)
    System.out.println(value);
}
// Итого: O(1 + 2 + 3 + ... + n) = O(n²)
// Для 1000 элементов: 500,000 операций!

✓ Правильный способ: Iterator

// БЫСТРО: O(n)
Iterator<Integer> iterator = linkedList.iterator();
while (iterator.hasNext()) {
    int value = iterator.next();  // O(1)
    System.out.println(value);
}

// Или for-each (использует Iterator)
for (Integer value : linkedList) {  // O(n)
    System.out.println(value);
}

Почему Iterator быстрее?

// Iterator хранит текущую позицию
private class ListItr implements ListIterator<E> {
    private Node<E> lastReturned = null;
    private Node<E> next;  // Текущая позиция
    
    public E next() {
        lastReturned = next;  // O(1) - просто переходим к следующему
        next = next.next;     // O(1) - по ссылке
        return lastReturned.item;
    }
}

// vs get(index) каждый раз ищет с начала
// get(0) - от первого элемента
// get(1) - снова от первого! (не помнит где был)
// get(2) - снова от первого!

Выводы

LinkedList.get(index):

  • ✗ O(n) сложность
  • ✗ Никогда не используй в цикле по индексам
  • ✓ Используй Iterator для итерации
  • ✓ Хорош для add/remove в начало/конец

Используй LinkedList для:

  • Очередей (Queue, Deque)
  • Частого добавления/удаления в начало/конец
  • Итерации через Iterator

Используй ArrayList для:

  • Случайного доступа по индексу
  • Большинства других случаев