Перейти к содержанию
Educora
Продвинутый18 мин11 / 18

Коллекции и дженерики

Используй гибкие структуры данных — ArrayList, HashMap и HashSet — и узнай, как дженерики обеспечивают безопасность типов.

Проверь себя
В этом уроке ты узнаешь
  • Добавлять, удалять, изменять и сортировать элементы в ArrayList
  • Хранить пары «ключ–значение» в HashMap, а уникальные значения — в HashSet
  • Понимать обобщённые типы вроде List<String> и писать простой обобщённый класс

Длина массива фиксирована: в массив на 5 ячеек нельзя добавить 6-й элемент. Но корзина покупок, сообщения в чате или список игроков постоянно растут и уменьшаются. Для этого в Java есть готовые коллекции — гибкие структуры данных из пакета java.util.

ArrayList: растущий список

ArrayList работает с индексами, как массив, но сам растёт по мере добавления элементов. <String> в угловых скобках указывает, какого типа элементы хранит список. Переменную обычно объявляют с типом общего интерфейса List, а справа пишут пустые <> («ромб»), чтобы не повторять тип.

Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> cities = new ArrayList<>();
        cities.add("Baku");
        cities.add("Ganja");
        cities.add("Lankaran");
        cities.add(1, "Shaki");
        System.out.println(cities + " size=" + cities.size());
        System.out.println(cities.get(0) + ", " + cities.contains("Ganja"));
        cities.remove("Lankaran");
        cities.set(0, "Sumgait");
        Collections.sort(cities);
        System.out.println(cities);
        for (String c : cities) {
            System.out.print(c.toUpperCase() + " ");
        }
        System.out.println();
    }
}
Ожидаемый результат
[Baku, Shaki, Ganja, Lankaran] size=4
Baku, true
[Ganja, Shaki, Sumgait]
GANJA SHAKI SUMGAIT 
МетодЧто делает
add(x), add(i, x)добавляет в конец или на позицию i
get(i), set(i, x)читает или заменяет элемент с индексом i
remove(i), remove(x)удаляет по индексу или по значению
size(), isEmpty()количество элементов, пуст ли список
contains(x), indexOf(x)есть ли элемент и где он
Интерактив
Загрузка симуляции…
list.contains(x) проверяет элементы по одному — именно как этот линейный поиск. Чем длиннее список, тем дольше поиск. Если часто нужно спрашивать «есть ли такой элемент?», гораздо быстрее HashSet, о котором ниже.

HashMap: ключи и значения

Отображение (Map) связывает каждый ключ со значением — как словарь: слово → толкование, страна → столица. Ключи не повторяются: если снова вызвать put с тем же ключом, старое значение заменится. get возвращает null, если ключ не найден, а getOrDefault — запасное значение, которое ты укажешь.

Java
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        Map<String, String> capitals = new HashMap<>();
        capitals.put("Azerbaijan", "Baku");
        capitals.put("Turkiye", "Ankara");
        capitals.put("Georgia", "Tbilisi");
        System.out.println(capitals.get("Turkiye"));
        System.out.println(capitals.get("France"));
        System.out.println(capitals.getOrDefault("France", "unknown"));
        System.out.println(capitals.containsKey("Georgia") + " " + capitals.size());

        String text = "to be or not to be";
        Map<String, Integer> counts = new TreeMap<>();
        for (String word : text.split(" ")) {
            counts.put(word, counts.getOrDefault(word, 0) + 1);
        }
        System.out.println(counts);
        for (Map.Entry<String, Integer> e : counts.entrySet()) {
            System.out.println(e.getKey() + " -> " + e.getValue());
        }
    }
}
Ожидаемый результат
Ankara
null
unknown
true 3
{be=2, not=1, or=1, to=2}
be -> 2
not -> 1
or -> 1
to -> 2

Во второй части считаем слова: split(" ") разбивает текст на слова, и счётчик каждого слова растёт на 1. Здесь использован TreeMap — это тоже Map, но он хранит ключи в алфавитном порядке. HashMap хранит элементы без определённого порядка: он быстрее, но порядок вывода заранее неизвестен. entrySet() позволяет перебрать в цикле все пары «ключ–значение».

HashSet: уникальные элементы

Множество (Set) хранит каждый элемент только один раз. add возвращает true, если элемент добавлен, и false, если он уже был. В HashSet метод contains работает намного быстрее, чем в списке: он не перебирает элементы, а с помощью хеш-кода сразу смотрит в нужное место.

Java
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        List<String> visitors = List.of("Leyla", "Murad", "Leyla", "Aysel", "Murad");
        Set<String> unique = new HashSet<>(visitors);
        System.out.println(visitors.size() + " visits, " + unique.size() + " people");
        System.out.println(unique.add("Elvin") + " " + unique.add("Leyla"));
        System.out.println(unique.contains("Aysel"));
        System.out.println(new TreeSet<>(unique));
    }
}
Ожидаемый результат
5 visits, 3 people
true false
true
[Aysel, Elvin, Leyla, Murad]

Дженерики

List<String>, Map<String, Integer> — это обобщённые типы (дженерики). Благодаря им компилятор не позволит положить в список элемент не того типа, и при извлечении элемента не нужно приводить тип. Коллекции хранят только объекты, поэтому вместо примитивов пишут классы-обёртки: Integer, Double, Boolean, Character. Преобразование int ↔ Integer Java выполняет автоматически (autoboxing).

Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> T last(List<T> list) {
        return list.get(list.size() - 1);
    }

    public static void main(String[] args) {
        Pair<String, Integer> p = new Pair<>("Aysel", 95);
        System.out.println(p.first() + " scored " + p.second());
        List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
        int total = 0;
        for (int n : nums) {
            total += n;
        }
        System.out.println(total + " " + last(nums) + " " + last(List.of("a", "b")));
        nums.remove(1);
        System.out.println(nums);
        nums.remove(Integer.valueOf(10));
        System.out.println(nums);
    }
}

class Pair<A, B> {
    private final A first;
    private final B second;

    Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }

    A first() { return first; }
    B second() { return second; }
}
Ожидаемый результат
Aysel scored 95
60 30 b
[10, 30]
[30]
  • class Pair<A, B> — обобщённый класс: A и B — параметры типа, которые при создании объекта заменяются конкретными типами.
  • static <T> T last(List<T> list) — обобщённый метод: один и тот же код работает и для списка Integer, и для списка String.
  • List.of(...) одной строкой создаёт неизменяемый список; если его нужно менять, пиши new ArrayList<>(List.of(...)).

Главное

  • ArrayList — список переменного размера: add, get, set, remove, size.
  • HashMap связывает ключи со значениями; ключи уникальны, а get возвращает null, если ключа нет.
  • HashSet хранит только уникальные элементы и очень быстро выполняет contains.
  • HashMap и HashSet не сохраняют порядок; отсортированные варианты — TreeMap и TreeSet.
  • Дженерики (List<String>) обеспечивают безопасность типов; вместо примитивов используют классы-обёртки Integer, Double.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Чем коллекция вроде ArrayList отличается от массива?