JavaRush /Курси /JAVA 25 SELF /Нові колекції: SequencedCollection, SequencedSet, Sequenc...

Нові колекції: SequencedCollection, SequencedSet, SequencedMap

JAVA 25 SELF
Рівень 34 , Лекція 4
Відкрита

1. Проблема порядку в класичних колекціях

У Java завжди були колекції, які гарантують порядок елементів (наприклад, ArrayList, LinkedList, LinkedHashSet, LinkedHashMap), і такі, де порядок не гарантується (наприклад, HashSet, HashMap). Але у всіх цих колекціях був один спільний недолік: хоча деякі з них зберігають елементи в певному порядку, стандартні інтерфейси (List, Set, Map) не надавали універсальних методів для доступу до перших і останніх елементів або для розвороту порядку.

Наприклад, якщо у вас є List<String>, ви можете отримати перший елемент через list.get(0), але для Set або Map такий прийом уже не спрацює — доведеться використовувати ітератори або писати зайвий код. Погодьтеся, це незручно й шкодить читабельності коду.

Це ніби у вас шафа з шухлядами, але щоб дістати першу чи останню, доводилося б щоразу перераховувати їх вручну. Чудово було б мати спеціальні ручки «перший» і «останній», чи не так?

Поява SequencedCollection та повʼязаних інтерфейсів

У Java 21 зʼявилися нові інтерфейси колекцій:

  • SequencedCollection<E>
  • SequencedSet<E>
  • SequencedMap<K, V>

Ці інтерфейси розширюють стандартні колекції та запроваджують єдиний підхід до роботи з порядком елементів. Тепер можна писати універсальний код для будь-яких колекцій, де порядок має значення, не замислюючись про конкретну реалізацію.

Що це таке?

  • SequencedCollection — це колекція, у якій елементи мають визначений порядок, і ви можете легко отримати перший і останній елемент, а також розвернути порядок.
  • SequencedSet — те саме, але для множин (унікальні елементи).
  • SequencedMap — те саме, але для відображень (ключ-значення).

Які колекції тепер їх реалізують?

У Java 21 нові інтерфейси реалізують такі стандартні колекції:

  • ArrayList, LinkedListSequencedCollection
  • LinkedHashSet, TreeSetSequencedSet
  • LinkedHashMap, TreeMapSequencedMap

Це означає, що якщо ви вже використовуєте ці колекції, ви отримуєте нові можливості автоматично.

2. Основні методи SequencedCollection, SequencedSet, SequencedMap

Методи SequencedCollection

E getFirst();      // Отримати перший елемент
E getLast();       // Отримати останній елемент
SequencedCollection<E> reversed(); // Отримати колекцію у зворотному порядку

Методи SequencedSet

Ті самі методи, що й у SequencedCollection, плюс усе, що робить Set.

Методи SequencedMap

Map.Entry<K, V> firstEntry();    // Перший елемент (ключ-значення)
Map.Entry<K, V> lastEntry();     // Останній елемент (ключ-значення)
SequencedMap<K, V> reversed();   // Мапу у зворотному порядку

3. Приклади використання нових інтерфейсів

Приклад 1: Отримання першого та останнього елемента

import java.util.*;

public class SequencedDemo {
    public static void main(String[] args) {
        SequencedCollection<String> sc = new ArrayList<>();
        sc.add("Java");
        sc.add("Python");
        sc.add("Kotlin");

        // Отримати перший та останній елемент
        String first = sc.getFirst(); // "Java"
        String last = sc.getLast();   // "Kotlin"

        System.out.println("Перший: " + first);
        System.out.println("Останній: " + last);
    }
}

Виведення:

Перший: Java
Останній: Kotlin

Приклад 2: Розворот колекції

SequencedCollection<String> sc = new LinkedList<>();
sc.add("A");
sc.add("B");
sc.add("C");

SequencedCollection<String> reversed = sc.reversed();
System.out.println(reversed); // [C, B, A]

Зверніть увагу: reversed() повертає подання колекції у зворотному порядку. Якщо змінити вихідну колекцію, зміниться й зворотне подання!

Приклад 3: Робота з SequencedSet

SequencedSet<Integer> set = new LinkedHashSet<>();
set.add(100);
set.add(200);
set.add(300);

System.out.println("Перший елемент: " + set.getFirst()); // 100
System.out.println("Останній елемент: " + set.getLast()); // 300

SequencedSet<Integer> reversedSet = set.reversed();
System.out.println(reversedSet); // [300, 200, 100]

Приклад 4: Робота з SequencedMap

SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("apple", 5);
map.put("banana", 3);
map.put("cherry", 7);

Map.Entry<String, Integer> first = map.firstEntry();
Map.Entry<String, Integer> last = map.lastEntry();

System.out.println("Перший: " + first.getKey() + " = " + first.getValue()); // apple = 5
System.out.println("Останній: " + last.getKey() + " = " + last.getValue()); // cherry = 7

SequencedMap<String, Integer> reversedMap = map.reversed();
System.out.println(reversedMap); // {cherry=7, banana=3, apple=5}

4. Як це повʼязано з вашим застосунком?

Припустімо, у вашому навчальному застосунку ви зберігаєте список користувачів, які увійшли до системи, і хочете швидко отримати першого та останнього користувача (наприклад, для показу «хто першим увійшов» і «хто останнім»). Раніше доводилося писати щось на кшталт:

List<String> users = new ArrayList<>();
// ... додаємо користувачів
String first = users.get(0);
String last = users.get(users.size() - 1);

Але якщо колекція — це не список, а, наприклад, LinkedHashSet (де елементи унікальні й порядок збережено), такий прийом уже не спрацює:

Set<String> users = new LinkedHashSet<>();
// ... додаємо користувачів
// Як отримати перший елемент? Лише ітератором:
String first = users.iterator().next();
// А останній? Доведеться перебрати всі елементи!

Тепер усе простіше й універсальніше:

SequencedSet<String> users = new LinkedHashSet<>();
// ... додаємо користувачів
String first = users.getFirst();
String last = users.getLast();

Це не лише зменшує кількість коду, а й робить його читабельнішим і безпечнішим.

5. Схема нових інтерфейсів

classDiagram
    Collection <|-- SequencedCollection
    List <|-- SequencedCollection
    Set <|-- SequencedSet
    Map <|-- SequencedMap
    SequencedCollection <|-- SequencedSet
    SequencedSet <|-- LinkedHashSet
    SequencedSet <|-- TreeSet
    SequencedCollection <|-- ArrayList
    SequencedCollection <|-- LinkedList
    SequencedMap <|-- LinkedHashMap
    SequencedMap <|-- TreeMap

6. Корисні нюанси

Практичні переваги SequencedCollection

  • Єдиний інтерфейс для роботи з порядком: Більше не потрібно памʼятати, де доступний get(0), де потрібен ітератор, а де взагалі неможливо отримати перший елемент.
  • Зручність під час роботи з чергами та стеками: Легко отримати перший (head) і останній (tail) елемент.
  • Безпека й читабельність: Менше помилок, повʼязаних із неправильним використанням колекцій; код стає самодокументованим.
  • Швидкий розворот колекції: Метод reversed() дозволяє легко отримати зворотний порядок без ручних маніпуляцій.
  • Простота підтримки та розширення коду: Якщо в майбутньому ви захочете замінити, наприклад, ArrayList на LinkedHashSet, код, що використовує SequencedCollection, не доведеться переписувати.

Особливості реалізації

  • Не всі колекції реалізують SequencedCollection: Наприклад, HashSet і HashMap не гарантують порядок, тому не реалізують нові інтерфейси.
  • Методи можуть викидати винятки: Якщо колекція порожня, виклик getFirst() або getLast() призведе до NoSuchElementException. Не забудьте перевірити, що колекція не порожня!
  • reversed() — це подання, а не копія: Зміни вихідної колекції відображаються у зворотному поданні й навпаки.
  • Сумісність: Нові інтерфейси доступні лише починаючи з Java 21. Якщо ви використовуєте старішу версію Java, ці можливості вам поки недоступні (але це чудова нагода оновитися!).
  • Узагальнені типи (generics): Усі нові інтерфейси повністю їх підтримують, тож можна працювати з будь-якими типами даних.

7. Типові помилки під час роботи з SequencedCollection

Помилка № 1: Очікування підтримки порядку від колекцій, які його не гарантують. Якщо ви спробуєте перетворити HashSet на SequencedSet, отримаєте помилку компіляції — у HashSet немає порядку, і він не реалізує цей інтерфейс.

Помилка № 2: Ігнорування порожніх колекцій. Виклик getFirst() або getLast() на порожній колекції викине виняток. Перш ніж звертатися до цих методів, перевірте, що колекція не порожня:

if (!sc.isEmpty()) {
    String first = sc.getFirst();
}

Помилка № 3: Неправильне розуміння reversed(). Метод reversed() повертає подання, а не копію. Якщо ви зміните зворотне подання, зміниться й вихідна колекція (і навпаки). Це може призвести до неочікуваних результатів, якщо ви не очікуєте такої поведінки.

Помилка № 4: Використання нових інтерфейсів на старих версіях Java. Якщо ваш проєкт компілюється з версією нижче Java 21, компілятор не знайде ці інтерфейси. Перевірте версію JDK у налаштуваннях проєкту!

1
Опитування
Сучасні колекції, рівень 34, лекція 4
Недоступний
Сучасні колекції
Сучасні колекції та незмінюваність
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ