Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Как дела с коллекциями в Java
Коллекции — фундамент Java программирования. Рассмотрю их подробно и когда использовать каждую.
1. Иерархия коллекций
Collection
├── List (упорядоченные, могут быть дубликаты)
│ ├── ArrayList (массив, быстрый random access)
│ ├── LinkedList (связный список, быстрые add/remove в начало)
│ └── CopyOnWriteArrayList (потокобезопасная)
├── Set (без дубликатов, неупорядоченные)
│ ├── HashSet (O(1) поиск)
│ ├── TreeSet (отсортирована, O(log n))
│ └── LinkedHashSet (сохраняет порядок вставки)
└── Queue (FIFO/LIFO)
├── LinkedList (FIFO)
├── PriorityQueue (по приоритету)
└── Deque (оба конца)
Map (ключ-значение)
├── HashMap (O(1) поиск)
├── TreeMap (отсортирована, O(log n))
├── LinkedHashMap (сохраняет порядок)
└── ConcurrentHashMap (потокобезопасная)
2. Выбор нужной коллекции
ArrayList — дефолтный выбор
List<String> names = new ArrayList<>();
names.add("Иван");
names.add("Мария");
String first = names.get(0); // O(1)
Когда использовать:
- Нужен быстрый доступ по индексу
- Больше чтений, чем добавлений в начало
Недостатки:
- Медленное удаление из начала/конца
- Нужно увеличивать массив при переполнении
LinkedList — для очередей
Queue<String> queue = new LinkedList<>();
queue.add("first");
queue.add("second");
String polled = queue.poll(); // O(1)
Когда использовать:
- Нужна очередь (FIFO) или стек (LIFO)
- Много добавлений в конец и удаления из начала
Недостатки:
- Медленный random access O(n)
- Дополнительная память на связи
HashSet — для уникальных значений
Set<Integer> uniqueIds = new HashSet<>();
uniqueIds.add(1);
uniqueIds.add(2);
uniqueIds.add(1); // Не добавится, уже есть
boolean contains = uniqueIds.contains(1); // O(1)
Когда использовать:
- Нужны уникальные значения
- Нужна O(1) проверка наличия
Внимание:
- equals() и hashCode() должны быть правильными!
class User {
private Long id;
@Override
public boolean equals(Object o) {
if (!(o instanceof User)) return false;
return id.equals(((User) o).id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}
TreeSet — отсортированное множество
Set<Integer> sorted = new TreeSet<>();
sorted.add(3);
sorted.add(1);
sorted.add(2);
// Итерация: 1, 2, 3 (автоматически отсортировано)
Когда использовать:
- Нужны отсортированные данные
- Нужны операции как first(), last(), subSet()
Недостатки:
- O(log n) для add/remove/contains
- Нужен Comparable или Comparator
HashMap — основная карта
Map<String, Integer> userAges = new HashMap<>();
userAges.put("Иван", 30);
userAges.put("Мария", 25);
if (userAges.containsKey("Иван")) {
System.out.println(userAges.get("Иван")); // 30
}
userAges.forEach((name, age) ->
System.out.println(name + ": " + age)
);
Когда использовать:
- Нужны пары ключ-значение
- O(1) поиск и вставка
Внимание: Не потокобезопасна!
ConcurrentHashMap — потокобезопасная
Map<String, Integer> counts = new ConcurrentHashMap<>();
counts.put("key1", 1);
// Безопасна из разных потоков
counts.computeIfPresent("key1", (k, v) -> v + 1);
Когда использовать:
- Многопоточное приложение
- Нужна высокая производительность
3. Операции над коллекциями
Потоковая обработка (Streams)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Фильтрация
List<Integer> even = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList()); // [2, 4]
// Трансформация
List<String> strings = numbers.stream()
.map(n -> "num" + n)
.collect(Collectors.toList()); // [num1, num2, ...]
// Группировка
Map<Integer, List<Integer>> grouped = numbers.stream()
.collect(Collectors.groupingBy(n -> n % 2)); // 0 -> [2, 4], 1 -> [1, 3, 5]
// Поиск
Optional<Integer> first = numbers.stream()
.filter(n -> n > 3)
.findFirst(); // Optional(4)
// Расчёты
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum(); // 15
Итерирование
List<String> items = Arrays.asList("a", "b", "c");
// for-each (рекомендуется)
for (String item : items) {
System.out.println(item);
}
// iterator (для удаления)
Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (item.equals("b")) {
iterator.remove(); // Безопасно удаляет
}
}
// forEach (функциональный)
items.forEach(System.out::println);
4. Производительность
| Операция | ArrayList | LinkedList | HashSet | TreeSet | HashMap |
|---|---|---|---|---|---|
| Get(index) | O(1) | O(n) | - | - | - |
| Add | O(1)* | O(1) | O(1)* | O(log n) | O(1)* |
| Remove | O(n) | O(1) | O(1) | O(log n) | O(1) |
| Contains | O(n) | O(n) | O(1) | O(log n) | O(1) |
*Амортизированная сложность
5. Частые ошибки
Ошибка 1: Неправильный equals/hashCode
// ПЛОХО - HashSet не работает правильно
class User {
private String name;
// Нет equals/hashCode!
}
// ХОРОШО
class User {
private String name;
@Override
public boolean equals(Object o) {
return o instanceof User && name.equals(((User) o).name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
Ошибка 2: ConcurrentModificationException
// ПЛОХО - исключение!
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String item : list) {
if (item.equals("b")) {
list.remove(item); // ConcurrentModificationException
}
}
// ХОРОШО
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) {
it.remove(); // Безопасно
}
}
Ошибка 3: Синхронизация
// ПЛОХО - при многопоточности
Map<String, Integer> map = new HashMap<>();
// ХОРОШО - потокобезопасно
Map<String, Integer> map = Collections.synchronizedMap(new HashMap<>());
// или ещё лучше
Map<String, Integer> map = new ConcurrentHashMap<>();
6. Рекомендации
- Начинайте с ArrayList/HashMap — это дефолтный выбор в 90% случаев
- Используйте Set для уникальности — просто и эффективно
- Проверяйте equals/hashCode — это источник множества ошибок
- Потокобезопасность — используйте Concurrent* версии или Collections.synchronized*
- Streams — модный способ, но иногда простой цикл понятнее
- Профилируйте — выбирайте структуру данных по реальным характеристикам нагрузки