Перейти к содержимому
Теория · Массивы · урок 3 из 8

Обход массива и Arrays

Массив редко читают по одному элементу. Обычно нужно что-то сделать со всеми элементами сразу: сложить, напечатать, отсортировать.

Вернёмся к ряду шкафчиков. Чтобы проверить все шкафчики, вы идёте вдоль ряда от первого к последнему и открываете каждый по очереди. Для массива такую прогулку делает цикл: на каждом круге он берёт следующий индекс. Посчитаем сумму баллов:

JAVA
public class Main {
    public static void main(String[] args) {
        int[] scores = {90, 75, 60};
        int sum = 0;
        for (int i = 0; i < scores.length; i++) {
            sum = sum + scores[i];
        }
        System.out.println(sum); // 225
    }
}

Давайте разберём программу по строкам:

  1. int sum = 0; заводит сумму, пока нулевую.
  2. for (int i = 0; i < scores.length; i++) проходит индексы 0, 1 и 2: ровно столько кругов, сколько элементов в массиве.
  3. sum = sum + scores[i]; прибавляет к сумме элемент с текущим индексом.
  4. После цикла System.out.println(sum) печатает 225.

Цикл по индексам

У такого цикла три правила:

  • счёт начинается с 0, с первого индекса;
  • условие — i < scores.length, строго меньше: последний индекс на единицу меньше длины;
  • через индекс элементы можно не только читать, но и менять: scores[i] = scores[i] * 2; удваивает очередной элемент.

С условием i <= scores.length цикл сделает лишний круг. На нём i станет равной 3, и чтение scores[3] бросит исключение ArrayIndexOutOfBoundsException.

Цикл for-each

Когда индекс не нужен, есть запись короче. Цикл for-each по очереди кладёт в переменную каждый элемент массива, от первого до последнего. Та же сумма с ним выглядит так:

JAVA
int total = 0;
for (int x : scores) {
    total = total + x;
}
System.out.println(total); // 225

Заголовок for (int x : scores) читается как «для каждого x из scores»:

  • int x — переменная для очередного элемента, её тип совпадает с типом элементов;
  • двоеточие отделяет переменную от массива;
  • scores — массив, который обходит цикл.

Счётчика, условия и шага здесь нет: цикл сам знает, где начало и конец массива. Поэтому выйти за границу в нём нельзя.

Переменная x получает копию элемента, как b в int b = a получает копию числа. Это обычная переменная: её можно изменить, например написать x = 0. Но массив от этого не изменится. Это как переписать число из шкафчика в блокнот: исправление в блокноте не меняет того, что лежит в шкафчике. Поэтому for-each подходит, чтобы читать элементы, а менять их нужно циклом по индексам.

Импорт и класс Arrays

Стандартная библиотека Java — это тысячи готовых классов. Они разложены по пакетам, как книги по полкам: у каждого класса есть полное имя из названия пакета и своего имени. Класс Arrays с инструментами для массивов лежит в пакете java.util, и его полное имя — java.util.Arrays.

Классы System, String и Integer лежат в пакете java.lang. Его Java подключает в каждую программу сама, поэтому до сих пор нам ничего подключать не приходилось. Для класса из другого пакета в начале файла, до public class Main, пишут импорт:

JAVA
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] scores = {90, 75, 60};
        System.out.println(Arrays.toString(scores)); // [90, 75, 60]
        Arrays.sort(scores);
        System.out.println(Arrays.toString(scores)); // [60, 75, 90]
    }
}

Здесь картинка с полками неточна: импорт не снимает класс с полки и не копирует его код в программу. Он только сообщает компилятору, что короткое имя Arrays означает java.util.Arrays. Без этой строки компилятор не узнает имя Arrays и остановится с ошибкой cannot find symbol — той же, что для необъявленной переменной.

Arrays.toString

Arrays.toString(scores) собирает из элементов строку в квадратных скобках через запятую: [90, 75, 60]. Её удобно печатать, когда нужно увидеть весь массив сразу.

Без этого метода не обойтись. Строка System.out.println(scores) печатает не элементы, а служебную запись вида [I@ с набором цифр и букв. Это обозначение типа и внутренний номер массива, и этот номер может меняться от запуска к запуску.

Arrays.sort

Arrays.sort(scores) сортирует элементы по возрастанию. Метод меняет сам массив, а не создаёт новый, и ничего не возвращает. Поэтому вызов пишут отдельной инструкцией, а результат смотрят через Arrays.toString. Запись System.out.println(Arrays.sort(scores)) не скомпилируется.

Частая ошибка

Хотели удвоить все цены и записали новое значение в переменную for-each:

JAVA
int[] prices = {10, 20, 30};
for (int p : prices) {
    p = p * 2; // ✗
}
System.out.println(Arrays.toString(prices)); // [10, 20, 30]

Программа компилируется и печатает [10, 20, 30]: цены не изменились. Каждый круг удваивал копию элемента в переменной p, а массив оставался прежним. Чтобы изменить элементы, нужен цикл по индексам: prices[i] = prices[i] * 2;.

Главное

  • Цикл по индексам идёт от 0 при условии i < a.length; через a[i] элементы можно читать и менять.
  • Цикл for-each for (int x : a) кладёт в x копию каждого элемента: запись в x массив не меняет.
  • Класс из другого пакета подключают импортом в начале файла: import java.util.Arrays;.
  • Arrays.toString превращает массив в строку [90, 75, 60], а Arrays.sort сортирует сам массив по возрастанию.