JavaRush /Курсы /Java Collections /Реализации интерфейса List

Реализации интерфейса List

Java Collections
6 уровень , 5 лекция
Открыта

— Если ты думаешь, что на интерфейсе List все закончилось, то ты ошибаешься, все только начинается. Давай я тебе расскажу про коллекции LinkedList и ArrayList.

Начну с коллекции ArrayList.

Вот как эта коллекция выглядит на схеме наследования:

Реализации интерфейса List 1

Зеленым отмечены интерфейсы.

Фиолетовым – абстрактные классы.

Красным – обычные классы.

Сплошная линия – наследование, пунктирная – реализация интерфейса.

Это самая простая коллекция. Внутри ArrayList хранит элементы в простом массиве.

Основное преимущество такой коллекции над массивом – это расширяемость – увеличение длины при надобности.

Если в этом массиве заканчивается место, то создаётся второй массив побольше, куда копируются все элементы из первого. Затем второй массив занимает место первого, а первый – выбрасывается (будет уничтожен сборщиком мусора).

— А насколько увеличивается массив?

— Длина нового массива рассчитывается так (3*n)/2+1, где n – это длина старого массива. Т.е. если старый массив был длиной 100 элементов, то новый будет 300/2+1 = 151.

При добавлении элемента в середину ArrayList, все элементы справа от него копируются на 1 позицию вправо, а затем в пустую ячейку добавляется новый элемент.

При удалении элемента из середины, все элементы справа от него копируются на 1 позицию влево.

— Ты говоришь, что ArrayList сам удлиняется, при добавлении элементов в него. А при удалении он сам укорачивается?

— Нет, сам внутренний массив в ArrayList никогда не укорачивается, но можно заставить ArrayList уменьшить свой внутренний массив до минимального уровня, если вызвать метод trimToSize().

Ну, и конечно, расскажу о LinkedList.

Вот как выглядит его схема наследования:

Зеленым отмечены интерфейсы.

Фиолетовым – абстрактные классы.

Красным – обычные классы.

Сплошная линия – наследование, пунктирная – реализация интерфейса.

Как ты уже знаешь, LinkedList хранит элементы в виде связного списка.

— Я постоянно об этом слышу, но не могла бы ты рассказать, что это такое?

— Конечно. Все просто.

Связный список состоит из одинаковых элементов, которые а) хранят данные, б) хранят ссылки на следующий и предыдущий элементы.

Вот так бы выглядел класс такого элемента для хранения строк:

Пример Описание
class LinkedListElement
{
String data;
LinkedListElement next;
LinkedListElement previous;
}
data хранит строковое значение элемента.
next хранит ссылку на следующий элемент в списке.
previous хранит ссылку на предыдущий элемент в списке.

А если использовать generics для типа данных, то будет что-то типа такого:

Класс элемента связного списка с generic’ом
class LinkedListElement<T>
{
 T data;
 LinkedListElement<T> next;
 LinkedListElement<T> previous;
}

— Ясненько.

— Давай, для лучшего понимания, напишем код, где добавим в двусвязный список 10 элементов:

Пример
public static void main(String[] args)
{ 
 LinkedListElement<Integer> tail; //хвост (самый последний элемент) списка

 for(int i=0;i<10;i++)
 {
  LinkedListElement<Integer> element = new LinkedListElement<Integer>();
  element.data = i;

  if (tail == null) //если в хвосте нет элементов, сделать наш элемент последним
  {
   tail = element;
  }
  else //если хвост есть, добавить элемент
  {
   tail.next = element; //добавляем хвосту ссылку на следующий элемент
 element.previous = tail; //добавляем новому элементу ссылку на хвост
   tail = element; //объявляем новый элемент хвостом.
  }
 }
}

Допустим у нас в списке 10 элементов, вот как вставить элемент в середину:

Реализации интерфейса List - 3

Ярко-красным выделены ссылки, которые поменялись – они теперь указывают на новый элемент.

Ярко-фиолетовым выделены новые ссылки – ссылки нового элемента на его соседей.

А теперь – кодом:

Вставка элемента в середину связного списка
//тут содержится элемент – голова списка
LinkedListElement<Integer> head = … 

//получаем 4-й элемент (нумерация с нуля)
LinkedListElement<Integer> element4 = head.next.next.next.next; 
//получаем 5-й элемент
LinkedListElement<Integer> element5 = element4.next;

//Создаем новый элемент, который будем вставлять
LinkedListElement<Integer> newElement = new LinkedListElement<Integer>();
newElement.data = -18;

//обмениваемся ссылками с элементом слева
newElement.previous = element4;
element4.next = newElement;

//обмениваемся ссылками с элементом справа
newElement.next = element5;
element5.previous = newElement;

— Спасибо, Элли, действительно узнал о списках много нового.

Комментарии (45)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
invoker main Уровень 42
20 октября 2025
как же я люблю линкед лист, а еще больше пугает что тут почти 2 года нет комментариев.
Anonymous #3336441 Уровень 50
26 января 2024
а если нам нужно добавить элемент на 100-ую позицию, нам надо 100 раз писать .next? или есть метод в который можно передать индекс и получить элемент списка?
Gans Electro Уровень 41
30 января 2024
Все верно. У LinkedList - ов быстрая вставка и удаление, а чтение медленное. Лекция JavaRush Но думаю можно где-то хранить список ссылок скажем на каждый тысячный элемент и тогда можно занчительно ускорить чтение и чтение-вставку.
Виктор Уровень 1
4 января 2023
Блин, что за запись "(3*n)/2+1". В полтора раза увеличивается + 1 элемент, которым, в принципе, можно и пренебречь
Anonymous #3183325 Уровень 51
1 сентября 2023
если массив будет длиной 1, то нельзя. Нужно будет или +1 или округление. Хотя запись можно было бы сделать и по красивей, например (3/2)*n + 1
Dolivo Serg Уровень 40
17 августа 2022
"Связный список состоит из одинаковых элементов..." Эти элементы (LinkedListElement) это простая структура данных, которую называют Node.
Andrey Karelin Уровень 41
3 мая 2022
И все это нам рассказывают уже после задач на деревья
RomanGV Уровень 27
18 июня 2023
Кстати, именно так я хранил дерево в тех задачах. Без списков и массивов. Пришлось поломать голову, но валик принял задачу)

    static class Entry<T> {
        private String elementName;
        private boolean availableToAddLeftChildren, availableToAddRightChildren;
        private Entry<T> parent, leftChild, rightChild; // вот оно! Предыдущий и два следующих

        public Entry (String name) {
            this.elementName = name;
            availableToAddLeftChildren = true;
            availableToAddRightChildren = true;
        }
Panda Dzho Уровень 51
28 апреля 2022
Оставлю здесь ссыль на человека, который расписал очень доступно про основные структуры данных. Тык
Mykhailo_Trofimov Уровень 42
2 февраля 2022
И ни намека в картинке о implements Deque<E> LinkedList-ом. Мелочь, скажете вы, а на собесах цепляются.
Max Zap Уровень 41
11 ноября 2021
На этом уровне читать про различные реализации List одно удовольствие)
Иван Уровень 3
7 сентября 2021
Господа знатоки, подскажите почему List именно наследует (extends) интерфейс Collection, а не реализовывает (implements). Обычно наследуем классы. Что то я видимо упустил дойдя до 36 уровня
Кисмер Уровень 41
8 сентября 2021
интерфейсы расширяют (extends) интерфейсы классы расширяют (extends) классы классы реализуют (implements) интерфейсы
LuneFox Уровень 41 Expert
9 февраля 2022
Интерфейс не может имплементировать другой интерфейс, потому что интерфейс это ещё не имплементация :)
Stepan Уровень 27
20 марта 2022
Интерфейс по своей сути не может реализовывать другой интерфейс, а только расширять. Так же класс не может реализовывать другой класс, а только расширять.
VitalyK #1116124 Уровень 1
1 сентября 2021
принцип понятен, не понятно как это может работать (или я что-то пропустил :) ), допустим "ссылка . ссылка" tail.next = element; или LinkedListElement<Integer> element4 = head.next.next.next.next;