← Назад к вопросам

Чем отличаются List, Set и Map в Java?

1.3 Junior🔥 231 комментариев
#Коллекции

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# List, Set и Map в Java: Полное Сравнение

Это три основных интерфейса коллекций в Java Collection Framework, каждый с разными характеристиками и применениями.

List: Упорядоченная коллекция

Характеристики List

List — это упорядоченная коллекция (последовательность), которая:

  • Допускает дубликаты — один элемент может быть несколько раз
  • Имеет индексы — доступ по позиции (0, 1, 2...)
  • Сохраняет порядок вставки — элементы хранятся в том порядке, в котором были добавлены

Реализации List

// ArrayList: переменная длина, быстрый доступ по индексу
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("apple");  // дубликат разрешен
System.out.println(list1);  // [apple, banana, apple]
System.out.println(list1.get(0));  // apple (доступ по индексу)

// LinkedList: хороша для вставок/удалений в начало/конец
List<Integer> list2 = new LinkedList<>();
list2.add(1);
list2.add(2);
list2.add(1);  // дубликат
list2.remove(0);  // удаление первого элемента

// CopyOnWriteArrayList: потокобезопасна для чтения
List<String> list3 = new CopyOnWriteArrayList<>();
list3.add("value1");

// Collections.unmodifiableList: неизменяемый список
List<String> immutable = Collections.unmodifiableList(
    List.of("a", "b", "c")
);

Операции List

List<String> fruits = new ArrayList<>();
fruits.add("apple");       // добавить в конец
fruits.add(0, "mango");    // добавить по индексу
fruits.set(1, "orange");   // заменить элемент
String first = fruits.get(0);  // получить по индексу
fruits.remove(0);          // удалить по индексу
fruits.remove("apple");    // удалить по значению (первое вхождение)
int index = fruits.indexOf("banana");  // найти индекс
boolean contains = fruits.contains("apple");  // проверка наличия

Set: Уникальные элементы

Характеристики Set

Set — это коллекция уникальных элементов, которая:

  • Запрещает дубликаты — каждый элемент только один раз
  • Нет индексов — доступ только по значению
  • Порядок не гарантирован (кроме LinkedHashSet и TreeSet)

Реализации Set

// HashSet: быстрая проверка наличия, неупорядочен
Set<String> set1 = new HashSet<>();
set1.add("apple");
set1.add("banana");
set1.add("apple");  // игнорируется, уже есть
System.out.println(set1);  // [banana, apple] или [apple, banana] (порядок не определен)
set1.contains("apple");  // true, O(1)

// LinkedHashSet: сохраняет порядок вставки
Set<String> set2 = new LinkedHashSet<>();
set2.add("apple");
set2.add("banana");
set2.add("cherry");
System.out.println(set2);  // [apple, banana, cherry]

// TreeSet: отсортирована, требует Comparable/Comparator
Set<Integer> set3 = new TreeSet<>();
set3.add(5);
set3.add(2);
set3.add(8);
System.out.println(set3);  // [2, 5, 8]

// EnumSet: для enum значений, очень быстра
Set<Color> colors = EnumSet.of(Color.RED, Color.BLUE);

// CopyOnWriteArraySet: потокобезопасна
Set<String> threadSafe = new CopyOnWriteArraySet<>();

Операции Set

Set<String> set1 = new HashSet<>(List.of("a", "b", "c"));
Set<String> set2 = new HashSet<>(List.of("b", "c", "d"));

set1.add("e");          // добавить
set1.remove("a");       // удалить
boolean has = set1.contains("b");  // проверить наличие

// Операции с множествами
Set<String> union = new HashSet<>(set1);
union.addAll(set2);  // объединение

Set<String> intersection = new HashSet<>(set1);
intersection.retainAll(set2);  // пересечение

Set<String> difference = new HashSet<>(set1);
difference.removeAll(set2);  // разность

Map: Пары ключ-значение

Характеристики Map

Map — это коллекция пар ключ-значение, которая:

  • Ключи уникальны — один ключ может быть только один раз
  • Значения могут повторяться — разные ключи могут указывать на одно значение
  • Быстрый поиск по ключу

Реализации Map

// HashMap: быстрая, неупорядочена
Map<String, Integer> map1 = new HashMap<>();
map1.put("alice", 25);
map1.put("bob", 30);
map1.put("charlie", 25);  // разные ключи, одно значение - ОК
System.out.println(map1.get("alice"));  // 25

// LinkedHashMap: сохраняет порядок вставки
Map<String, Integer> map2 = new LinkedHashMap<>();
map2.put("alice", 25);
map2.put("bob", 30);
map2.put("charlie", 35);
for (String key : map2.keySet()) {
    System.out.println(key);  // alice, bob, charlie (в порядке вставки)
}

// TreeMap: отсортирована по ключам
Map<Integer, String> map3 = new TreeMap<>();
map3.put(3, "three");
map3.put(1, "one");
map3.put(2, "two");
System.out.println(map3);  // {1=one, 2=two, 3=three}

// ConcurrentHashMap: потокобезопасна
Map<String, Integer> threadSafe = new ConcurrentHashMap<>();

// Collections.unmodifiableMap: неизменяемая
Map<String, Integer> immutable = Collections.unmodifiableMap(
    Map.of("a", 1, "b", 2)
);

Операции Map

Map<String, String> map = new HashMap<>();

// Добавление и получение
map.put("name", "John");  // добавить/обновить
String value = map.get("name");  // получить значение

// Проверки
boolean hasKey = map.containsKey("name");  // есть ключ?
boolean hasValue = map.containsValue("John");  // есть значение?

// Удаление
map.remove("name");  // удалить по ключу

// Получение с дефолтом
String def = map.getOrDefault("age", "Unknown");  // если ключа нет, вернуть дефолт

// Итерация
for (String key : map.keySet()) {
    System.out.println(key + " -> " + map.get(key));
}

for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " -> " + entry.getValue());
}

map.forEach((key, value) -> System.out.println(key + " -> " + value));

Сравнительная таблица

КритерийListSetMap
ДубликатыРазрешеныЗапрещеныКлючи уникальны
ИндексыДа (0, 1, 2...)НетНет (но есть ключи)
ПорядокСохраняетсяНе гарантированНе гарантирован
ДоступПо индексу или значениюПо значениюПо ключу
ПрименениеПоследовательностиУникальные значенияПары данных
РеализацияArrayList, LinkedListHashSet, TreeSetHashMap, TreeMap
ПроизводительностьO(1) на индексO(1) поискO(1) поиск по ключу

Практические примеры

Пример 1: Хранение последовательности

// List идеален для последовательности
List<String> shopping = new ArrayList<>();
shopping.add("milk");
shopping.add("bread");
shopping.add("eggs");
shopping.forEach(System.out::println);
// milk
// bread
// eggs

Пример 2: Удаление дубликатов

List<Integer> numbers = List.of(1, 2, 2, 3, 3, 3, 4);

// Используем Set для удаления дубликатов
Set<Integer> unique = new HashSet<>(numbers);
System.out.println(unique);  // [1, 2, 3, 4]

// Или в Stream
Set<Integer> unique2 = numbers.stream()
    .collect(Collectors.toSet());

Пример 3: Кэширование данных

// Map идеален для кэширования
Map<String, UserData> cache = new HashMap<>();

public UserData getUser(String id) {
    if (cache.containsKey(id)) {
        return cache.get(id);  // из кэша
    }
    
    UserData user = loadFromDatabase(id);
    cache.put(id, user);  // добавить в кэш
    return user;
}

Пример 4: Подсчет частоты

List<String> words = List.of("apple", "banana", "apple", "cherry", "banana", "apple");

Map<String, Integer> frequency = new HashMap<>();
for (String word : words) {
    frequency.put(word, frequency.getOrDefault(word, 0) + 1);
}

System.out.println(frequency);
// {apple=3, banana=2, cherry=1}

Stream API с Collection

List<String> names = List.of("alice", "bob", "alice", "charlie");

// List -> Set (удаление дубликатов)
Set<String> uniqueNames = names.stream()
    .collect(Collectors.toSet());

// List -> Map (группировка по первой букве)
Map<Character, List<String>> grouped = names.stream()
    .collect(Collectors.groupingBy(s -> s.charAt(0)));

// List -> Map (преобразование в пары)
Map<String, Integer> nameLengths = names.stream()
    .distinct()
    .collect(Collectors.toMap(s -> s, String::length));

System.out.println(nameLengths);
// {alice=5, bob=3, charlie=7}

Выбор нужной коллекции

Используй List когда:

  • Нужна упорядоченная последовательность
  • Часто обращаешься по индексу
  • Допускаются дубликаты
  • Нужна информация о позиции элемента

Используй Set когда:

  • Нужны только уникальные элементы
  • Важна скорость проверки наличия
  • Порядок не важен (или используй LinkedHashSet/TreeSet)
  • Нужны операции с множествами (union, intersection, difference)

Используй Map когда:

  • Нужна связь между ключом и значением
  • Быстрый поиск по ключу
  • Кэширование данных
  • Подсчет частоты или агрегация

Заключение

  • List — для последовательностей и упорядоченных коллекций
  • Set — для уникальных элементов и операций с множествами
  • Map — для пар ключ-значение и быстрого поиска

Правильный выбор зависит от того, что вы хотите делать с данными.