Массивы и списки в Java: ArrayList и HashMap — бесплатный урок

Массивы и списки в Java

Бесплатный открытый урок: массивы фиксированной длины, списки ArrayList, словари HashMap и выход за границу индекса. Примеры проверены на JDK 21.

Массив: длина задаётся один раз

Массив хранит заданное число элементов одного типа. Индексы начинаются с нуля, поэтому последний элемент имеет номер length − 1. Длина — это поле length, а не метод.

import java.util.Arrays;

public class Massivy {
    public static void main(String[] args) {
        int[] ocenki = {5, 3, 4, 5, 2};

        System.out.println("Всего оценок: " + ocenki.length);
        System.out.println("Первая: " + ocenki[0]);
        System.out.println("Последняя: " + ocenki[ocenki.length - 1]);

        int summa = 0;
        for (int ocenka : ocenki) {
            summa += ocenka;
        }
        System.out.println("Сумма: " + summa);
        System.out.println("Среднее: " + (double) summa / ocenki.length);

        Arrays.sort(ocenki);
        System.out.println(Arrays.toString(ocenki));
    }
}
Всего оценок: 5
Первая: 5
Последняя: 2
Сумма: 19
Среднее: 3.8
[2, 3, 4, 5, 5]
  • Цикл for (int ocenka : ocenki) читается как «для каждой оценки из массива». Он короче обычного for, но не даёт номера элемента и не позволяет менять массив по индексу.
  • (double) summa нужен, чтобы деление не стало целочисленным: без приведения среднее было бы 3. Это та же ловушка, что в уроке о типах данных.
  • Печатать массив напрямую бесполезно: System.out.println(ocenki) выведет что-то вроде [I@2f92e0f4. Для читаемого вида есть Arrays.toString.
  • Arrays.sort сортирует массив на месте — исходный порядок теряется.

Выход за границу массива

Компилятор такую ошибку не поймает: индекс становится известен только во время работы. Программа падает с исключением:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
	at Vyhod.main(Vyhod.java:4)

Сообщение читают справа налево: длина массива 3, значит допустимые индексы — 0, 1 и 2, а программа попросила третий. Последняя строка указывает файл и номер строки, где это произошло.

Список: длина меняется

Когда число элементов заранее неизвестно, берут ArrayList. В угловых скобках указывают тип элементов — так компилятор проверит, что в список не попадёт посторонний объект.

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

public class Spisok {
    public static void main(String[] args) {
        List<String> gruppa = new ArrayList<>();
        gruppa.add("Анна");
        gruppa.add("Борис");
        gruppa.add("Вера");

        System.out.println(gruppa);
        System.out.println("Человек в группе: " + gruppa.size());
        System.out.println("Второй: " + gruppa.get(1));
        System.out.println("Есть Вера: " + gruppa.contains("Вера"));

        gruppa.remove("Борис");
        for (String imya : gruppa) {
            System.out.println(imya);
        }
    }
}
[Анна, Борис, Вера]
Человек в группе: 3
Второй: Борис
Есть Вера: true
Анна
Вера

Слева от знака равенства стоит List — интерфейс, справа ArrayList — конкретная реализация. Так принято: переменная описывает, что нужно, а не как это устроено внутри.

Осторожно с удалением из списка чисел: remove(1) удалит элемент с индексом 1, а remove(Integer.valueOf(1)) — само значение 1. Для строк такой двусмысленности нет.

Словарь: пары «ключ — значение»

HashMap хранит значения по ключу и находит их за одно обращение, без перебора всего списка.

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

public class Slovar {
    public static void main(String[] args) {
        Map<String, Integer> bally = new HashMap<>();
        bally.put("Анна", 92);
        bally.put("Борис", 78);
        bally.put("Вера", 85);

        System.out.println("Баллов у Анны: " + bally.get("Анна"));
        System.out.println("Баллов у Гриши: " + bally.getOrDefault("Гриша", 0));

        for (Map.Entry<String, Integer> zapis : new TreeMap<>(bally).entrySet()) {
            System.out.println(zapis.getKey() + " — " + zapis.getValue());
        }
    }
}
Баллов у Анны: 92
Баллов у Гриши: 0
Анна — 92
Борис — 78
Вера — 85

Важно: порядок обхода HashMap не определён и может отличаться на другой машине или в другой версии Java. Поэтому в примере словарь перед печатью завёрнут в TreeMap, который всегда идёт по возрастанию ключа. Если бы мы обходили bally напрямую, три последние строки могли выйти в любом порядке.

get для отсутствующего ключа вернёт null, и попытка сравнить его с числом уронит программу. getOrDefault избавляет от этой проверки.

length, length() и size()

Три похожих способа узнать размер легко перепутать: у массива это поле length, у строки — метод length(), у списка — метод size(). Ошибка ловится ещё компилятором:

int[] ocenki = {5, 3, 4};
System.out.println(ocenki.length());

Ответ компилятора — cannot find symbol: method length().

Задание для самопроверки

  1. Найдите в массиве чисел максимум и его индекс, не пользуясь Arrays.sort.
  2. Соберите список из десяти чисел и удалите из него все нечётные. Проверьте, что размер списка изменился.
  3. Посчитайте, сколько раз каждое слово встречается в массиве строк. Используйте HashMap и getOrDefault, а печатайте результат по отсортированным ключам.

Что дальше

Предыдущий урок — циклы: for, while и do-while, следующий и последний в разделе — классы и объекты. Все бесплатные занятия собраны в разделе «Уроки Java».