Какая реализация LinkedList используется в Java: односвязная или двусвязная?

Ответ

В Java java.util.LinkedList реализован как двусвязный список. Каждый узел содержит ссылки на предыдущий и следующий элементы, что обеспечивает эффективную навигацию в обоих направлениях.

Внутренняя структура узла:

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

Преимущества двусвязной реализации:

  • Добавление и удаление элементов с обоих концов за O(1)
  • Эффективная поддержка ListIterator с перемещением назад и вперед
  • Упрощение операций удаления произвольного узла (не требуется поиск предыдущего элемента)
Сравнение производительности: Операция LinkedList (двусвязный) Односвязный список
addFirst() / removeFirst() O(1) O(1)
addLast() / removeLast() O(1) O(n) (требуется обход)
Удаление произвольного элемента O(1) при наличии итератора O(n)
Потребление памяти Выше (две ссылки на узел) Ниже (одна ссылка)

Ответ 18+ 🔞

Да ты посмотри, какая хитрая жопа у этих ява-разработчиков! Взяли и сделали свой LinkedList — а он, сука, двусвязный! То есть каждый узелок там, как маньяк с двумя пистолетами, держит ссылку и на предыдущего, и на следующего. Ходи куда хочешь — вперёд, назад, вообще похуй!

Смотри, как они это внутри устроили, эти извращенцы:

private static class Node<E> {
    E item;        // Сам элемент, ну тут понятно
    Node<E> next;  // Следующий, блядь
    Node<E> prev;  // Предыдущий, ёпта!
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

И теперь, внимание, почему это овердохуище удобно:

  • Хочешь в начало добавить или с начала удалить? O(1), хуй с горы — взял и сделал.
  • Хочешь в конец? Тоже O(1), потому что с конца так же легко подъехать, блядь!
  • Итератор с возможностью шастать туда-сюда? Да пожалуйста, ёперный театр! ListIterator просто ликует.
  • Удалить какой-то левый элемент из середины, если итератор на него уже смотрит? Вообще красота — O(1), потому что ты уже знаешь и его предка, и его потомка. Не надо, сука, весь список с начала обшаривать!

А теперь, для полного пиздеца, сравним с односвязным списком, где у каждого узла только одна ссылка — вперёд, как у подвыпившего студента:

Что делаем LinkedList (двусвязный) Односвязный список (упрощённый)
addFirst() / removeFirst() O(1) — раз плюнуть O(1) — тоже норм
addLast() / removeLast() O(1) — потому что хвост помним O(n) — потому что надо от начала до конца допиздюриваться, блядь!
Удаление из середины O(1) (с итератором) — красота O(n) — ищи свищи предка, мудя!
Память жрёт Больше (две ссылки, ядрёна вошь!) Меньше (одна ссылка, скромняга)

Короче, выбор за тобой. Нужно быстро с концами работать и по списку туда-сюда шнырять — бери LinkedList, он двусвязный, блядь. Жалко памяти и работаешь только с головой — может, односвязный и сойдёт. Но в стандартной Java, брат, тебе дали сразу полный фарш, с двумя ссылками на узле. Пользуйся, не обляпайся!