Обобщения: List<String>
Чему вы научитесь:
- заводить список
List<String>и объяснять, что даёт тип в угловых скобках; - добавлять, менять и удалять элементы
ArrayListи обходить список, не ломая его; - объяснять, как
HashSetиHashMapнаходят элемент черезhashCodeиequals; - сортировать список своих объектов через
ComparableиCollections.sort.
Представьте гардероб в театре. Гардеробщик не знает заранее, сколько придёт зрителей: десять или триста. Поэтому шапки он кладёт на длинную полку, где хватит места всем, а пальто вешает на крючки с номерами.
Массив из темы о массивах для такого гардероба не годится. Это ряд шкафчиков, число которых задают один раз, при создании. Пришло больше гостей — нужен новый массив, и все вещи придётся перекладывать вручную.
Для таких задач в Java есть коллекции. Коллекция — объект, который хранит группу других объектов и сам растёт, когда их становится больше. Вся эта тема о коллекциях, и начнём мы со списка:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> guests = new ArrayList<>();
guests.add("Anna");
guests.add("Boris");
System.out.println(guests); // [Anna, Boris]
System.out.println(guests.size()); // 2
String first = guests.get(0);
System.out.println(first); // Anna
}
}Давайте разберём программу по строкам:
- Две строки
importподключают именаArrayListиListиз пакетаjava.util, где живут коллекции. List<String> guests = new ArrayList<>();создаёт пустой список строк.guests.add("Anna")кладёт строку в конец списка. Список растёт сам: его размер заранее никто не задавал.System.out.println(guests)печатает элементы в квадратных скобках:[Anna, Boris].guests.size()возвращает число элементов,2. У массива это полеlength, а у списка — методsize().guests.get(0)возвращает элемент с индексом0. Индексы, как и у массива, начинаются с нуля.
Тип в угловых скобках
Запись List<String> читается как «список строк». В угловых скобках стоит тип элементов, и он входит в тип самой переменной. Классы и интерфейсы, которые принимают тип как параметр, называют обобщениями. Тип в угловых скобках — это параметр типа.
Вернёмся в гардероб. Параметр типа — табличка над полкой: «Только шапки». Компилятор следит за табличкой строже любого гардеробщика и не даст положить на полку зонт:
List<String> guests = new ArrayList<>();
guests.add(42); // ✗error: incompatible types: int cannot be converted to StringОшибку ловит компилятор, ещё до запуска. Зато всё, что лежит в List<String>, точно строка. Поэтому guests.get(0) сразу возвращает String, и приводить тип вручную не нужно.
Справа, в new ArrayList<>(), угловые скобки пустые: тип компилятор сам берёт из левой части. Запись new ArrayList<String>() тоже верна, но длиннее.
List и ArrayList
Слева стоит List, а справа ArrayList, и это не опечатка. List — интерфейс, как Courier из темы об интерфейсах. Он перечисляет, что умеет любой список: add, get, size и другие методы. ArrayList — класс, который реализует этот интерфейс и хранит элементы в массиве.
Переменную объявляют с типом интерфейса, а конкретный класс называют только после new. Если позже понадобится другой список, поменяется одно слово справа, а код, который работает с guests, останется прежним. Почему слева List, а не ArrayList, — частый вопрос на собеседовании.
Числа в списке: List<Integer>
Параметром типа может быть только ссылочный тип, например класс, интерфейс или запись. Примитив вроде int туда не подходит, поэтому List<int> не скомпилируется:
error: unexpected typeНиже javac поясняет: required: reference, found: int. Нужен ссылочный тип, а найден примитив.
Для чисел берут класс-обёртку из темы об обёртках: List<Integer>. Остальное делает автоупаковка. При add она кладёт монету-int в конверт-Integer, а когда нужен int, достаёт её обратно:
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(7);
int sum = nums.get(0) + nums.get(1);
System.out.println(sum); // 12nums.add(5) упаковывает 5 в Integer. В строке с суммой get возвращает два Integer, и для сложения они распаковываются. С другими примитивами так же: List<Double>, List<Boolean>, List<Character>.
Здесь картинка с полкой неточна. На полке списка лежат не сами объекты, а ссылки на них, как в переменной из темы о классах. Поэтому один и тот же объект может лежать сразу в двух списках.
Частая ошибка
Импортировали только ArrayList:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
List<String> guests = new ArrayList<>(); // ✗
}
}error: cannot find symbolПрограмма не компилируется. Имя ArrayList компилятор знает, а List — нет, и ниже сообщения javac называет его: symbol: class List. Строка import java.util.ArrayList; подключает только имя ArrayList, поэтому здесь нужна ещё import java.util.List;.
Главное
- Коллекции из пакета
java.utilхранят группу объектов и растут сами;import java.util.ArrayList;подключает толькоArrayList, аList— отдельная строка. List<String>— список строк: в угловых скобках параметр типа, и другой тип компилятор в список не пустит.- Переменную объявляют интерфейсом
List, а объект создают черезnew ArrayList<>(); пустые скобки<>берут тип слева. - Параметр типа — только ссылочный тип:
List<int>не компилируется, а числа хранят вList<Integer>.