Зачем коллекции
Массивы имеют фиксированный размер. Коллекции из java.util растут динамически и дают готовые алгоритмы. Они хранят только объекты, для примитивов используются обертки.
Iterable└── Collection ├── List (упорядоченный, с повторами) ├── Set (без повторов) └── Queue (очередь)Map (пары ключ-значение, не наследует Collection)List
import java.util.ArrayList;import java.util.List;public class ListDemo { public static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("Анна"); names.add("Борис"); names.add("Вера"); names.add(1, "Глеб"); System.out.println(names); System.out.println(names.size()); System.out.println(names.get(0)); System.out.println(names.contains("Вера")); System.out.println(names.indexOf("Борис")); names.set(0, "Алиса"); names.remove("Вера"); names.remove(0); System.out.println(names); for (String name : names) { System.out.println(name); } System.out.println(names.isEmpty()); names.clear(); }}Фабричный метод List.of создает неизменяемый список:
List<Integer> fixed = List.of(1, 2, 3);fixed.add(4);Вторая строка бросит UnsupportedOperationException. Изменяемая копия: new ArrayList<>(List.of(1, 2, 3)).
Удаление по значению и по индексу
import java.util.ArrayList;import java.util.List;public class RemoveDemo { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(List.of(10, 20, 30, 40)); numbers.remove(1); System.out.println(numbers); numbers.remove(Integer.valueOf(40)); System.out.println(numbers); }}remove(int) удаляет по индексу, а для удаления по значению нужен Integer.valueOf.
Безопасное удаление при переборе
import java.util.ArrayList;import java.util.List;public class SafeRemove { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6)); numbers.removeIf(n -> n % 2 == 0); System.out.println(numbers); }}Удаление внутри for-each вызовет ConcurrentModificationException.
ArrayList и LinkedList
| ArrayList | LinkedList | |
|---|---|---|
| Доступ по индексу | быстрый | медленный |
| Вставка в конец | быстро | быстро |
| Вставка в середину | медленно | быстро после поиска места |
| Память | компактно | много накладных расходов |
В большинстве случаев используйте ArrayList.
Set
Множество не допускает дубликатов.
import java.util.HashSet;import java.util.List;import java.util.LinkedHashSet;import java.util.Set;import java.util.TreeSet;public class SetDemo { public static void main(String[] args) { Set<String> hash = new HashSet<>(); hash.add("яблоко"); hash.add("груша"); hash.add("яблоко"); System.out.println(hash.size()); Set<Integer> linked = new LinkedHashSet<>(List.of(3, 1, 2)); System.out.println(linked); Set<Integer> sorted = new TreeSet<>(List.of(3, 1, 2)); System.out.println(sorted); Set<Integer> a = new HashSet<>(Set.of(1, 2, 3, 4)); Set<Integer> b = Set.of(3, 4, 5); Set<Integer> union = new HashSet<>(a); union.addAll(b); Set<Integer> intersection = new HashSet<>(a); intersection.retainAll(b); Set<Integer> difference = new HashSet<>(a); difference.removeAll(b); System.out.println(union); System.out.println(intersection); System.out.println(difference); }}| Реализация | Порядок | Скорость |
|---|---|---|
HashSet | не гарантирован | O(1) |
LinkedHashSet | порядок добавления | O(1) |
TreeSet | отсортирован | O(log n) |
Элементы HashSet должны правильно реализовывать equals и hashCode.
Map
import java.util.HashMap;import java.util.Map;public class MapDemo { public static void main(String[] args) { Map<String, Integer> ages = new HashMap<>(); ages.put("Анна", 25); ages.put("Борис", 30); ages.put("Вера", 28); System.out.println(ages.get("Анна")); System.out.println(ages.get("Никто")); System.out.println(ages.getOrDefault("Никто", 0)); System.out.println(ages.containsKey("Борис")); System.out.println(ages.containsValue(28)); ages.put("Анна", 26); ages.remove("Вера"); System.out.println(ages.size()); for (Map.Entry<String, Integer> entry : ages.entrySet()) { System.out.println(entry.getKey() + " -> " + entry.getValue()); } for (String key : ages.keySet()) { System.out.println(key); } for (int value : ages.values()) { System.out.println(value); } ages.forEach((k, v) -> System.out.println(k + "=" + v)); }}Ключи уникальны. Повторный put заменяет значение.
Полезные методы Map
import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class MapMethods { public static void main(String[] args) { Map<String, Integer> counts = new HashMap<>(); String text = "to be or not to be"; for (String word : text.split(" ")) { counts.merge(word, 1, Integer::sum); } System.out.println(counts); Map<String, List<String>> groups = new HashMap<>(); groups.computeIfAbsent("фрукты", k -> new ArrayList<>()).add("яблоко"); groups.computeIfAbsent("фрукты", k -> new ArrayList<>()).add("груша"); System.out.println(groups); counts.putIfAbsent("new", 0); counts.compute("to", (k, v) -> v == null ? 1 : v + 10); System.out.println(counts); }}Реализации Map
| Реализация | Порядок ключей |
|---|---|
HashMap | не гарантирован |
LinkedHashMap | порядок добавления |
TreeMap | отсортированный по ключу |
Map.of(...) создает неизменяемую карту.
Queue и Deque
import java.util.ArrayDeque;import java.util.Deque;import java.util.LinkedList;import java.util.PriorityQueue;import java.util.Queue;public class QueueDemo { public static void main(String[] args) { Queue<String> queue = new LinkedList<>(); queue.offer("первый"); queue.offer("второй"); System.out.println(queue.peek()); System.out.println(queue.poll()); System.out.println(queue.size()); Deque<Integer> stack = new ArrayDeque<>(); stack.push(1); stack.push(2); stack.push(3); System.out.println(stack.pop()); System.out.println(stack.peek()); PriorityQueue<Integer> priority = new PriorityQueue<>(); priority.add(5); priority.add(1); priority.add(3); System.out.println(priority.poll()); System.out.println(priority.poll()); }}Для стека используйте ArrayDeque, а не устаревший Stack.
Сортировка и Comparator
import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;public class SortDemo { record Person(String name, int age) { } public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(List.of(5, 2, 8, 1)); Collections.sort(numbers); System.out.println(numbers); numbers.sort(Comparator.reverseOrder()); System.out.println(numbers); List<Person> people = new ArrayList<>(List.of( new Person("Борис", 30), new Person("Анна", 25), new Person("Вера", 25) )); people.sort(Comparator.comparingInt(Person::age)); System.out.println(people); people.sort(Comparator.comparingInt(Person::age).thenComparing(Person::name, Comparator.reverseOrder())); System.out.println(people); }}Класс может сам задать естественный порядок, реализовав Comparable<T>:
public class Version implements Comparable<Version> { private final int major; private final int minor; public Version(int major, int minor) { this.major = major; this.minor = minor; } @Override public int compareTo(Version other) { if (major != other.major) { return Integer.compare(major, other.major); } return Integer.compare(minor, other.minor); }}Утилитный класс Collections
import java.util.ArrayList;import java.util.Collections;import java.util.List;public class CollectionsDemo { public static void main(String[] args) { List<Integer> list = new ArrayList<>(List.of(4, 2, 9, 1)); System.out.println(Collections.max(list)); System.out.println(Collections.min(list)); Collections.reverse(list); Collections.shuffle(list); Collections.swap(list, 0, 1); System.out.println(Collections.frequency(list, 2)); List<Integer> readOnly = Collections.unmodifiableList(list); System.out.println(readOnly.size()); }}Выбор коллекции
- нужен упорядоченный список с доступом по индексу:
ArrayList; - нужны уникальные элементы:
HashSet, при необходимости порядка —LinkedHashSetилиTreeSet; - нужен поиск по ключу:
HashMap, с сортировкой —TreeMap; - нужна очередь или стек:
ArrayDeque; - нужен приоритет:
PriorityQueue.
Объявляйте переменную интерфейсным типом: List<String> list = new ArrayList<>();.
