$ sudo teach IT
МОДУЛЬ 5 · УРОК 2

Перебор массивов: цикл for и for-each

Научимся проходить по каждому элементу массива разными способами, заполнять, копировать и выводить массивы

~25 минут Для новичков Java Массивы

Зачем перебирать массивы?

Представьте, что учитель проверяет тетради 30 учеников. Он не может проверить только первую тетрадь и сказать «все хорошо». Он должен пройти через каждую тетрадь по очереди. Именно так же работают массивы: чтобы сделать что-то со всеми элементами, нужно пройти через каждый из них.

В предыдущем уроке мы научились обращаться к элементам массива по индексу: arr[0], arr[1], arr[2]. Но что делать, если в массиве 100 элементов? Писать 100 отдельных команд? Конечно, нет. Для этого существуют циклы — они позволяют автоматически проходить по каждому элементу массива.

Перебор массива — это процесс последовательного прохождения по всем его элементам от первого до последнего (или от последнего до первого). Это одна из самых частых операций в программировании. Без перебора невозможно: найти сумму чисел, найти максимальное значение, отфильтровать данные, распечатать список, проверить условия и многое другое.

Аналогия: Перебор массива — как проверка ячеек в почтовом отделении. Почтальон берёт одну письмо за другим, читает адрес и кладёт в нужный ящик. Он не может пропустить ни одно письмо — каждое должно быть обработано.

В Java есть два основных способа перебора массивов: классический цикл for с индексом и цикл for-each (усечённый for). Каждый из них имеет свои преимущества иlimitations. Давайте разберём оба способа подробно.

Классический цикл for по индексу

Классический цикл for — это самый универсальный способ перебора. Он даёт вам полный контроль над индексом: вы знаете, на каком элементе находитесь, можете менять направление обхода, пропускать элементы и обращаться к соседним ячейкам.

Синтаксис цикла for для перебора массива:

for (int i = 0; i < массив.length; i++) {
    // здесь работает переменная i как индекс
    // массив[i] — текущий элемент
}

Разберём каждую часть этого цикла:

  • int i = 0 — начальное значение: мы начинаем с индекса 0 (первый элемент)
  • i < массив.length — условие продолжения: цикл работает, пока i меньше длины массива
  • i++ — увеличение i на 1 после каждой итерации
  • массив[i] — обращение к текущему элементу внутри тела цикла

Пример: Выведем все элементы массива чисел на экран:

int[] numbers = {10, 20, 30, 40, 50};

for (int i = 0; i < numbers.length; i++) {
    System.out.println("Элемент [" + i + "] = " + numbers[i]);
}

Вывод:

Элемент [0] = 10
Элемент [1] = 20
Элемент [2] = 30
Элемент [3] = 40
Элемент [4] = 50

Как это работает пошагово:

  • Итерация 1: i = 0, numbers.length = 5, 0 < 5 → true, выводим numbers[0] = 10
  • Итерация 2: i = 1, 1 < 5 → true, выводим numbers[1] = 20
  • Итерация 3: i = 2, 2 < 5 → true, выводим numbers[2] = 30
  • Итерация 4: i = 3, 3 < 5 → true, выводим numbers[3] = 40
  • Итерация 5: i = 4, 4 < 5 → true, выводим numbers[4] = 50
  • Итерация 6: i = 5, 5 < 5 → false, цикл завершается

Важно: Свойство .length массива — это не метод, а поле. Вызывать его нужно без круглых скобок: numbers.length, а не numbers.length(). Это частая ошибка новичков, которые путают массивы со строками (у строк метод .length()).

Изменение элементов через цикл for

Одно из главных преимуществ классического цикла for — возможность не только читать, но и изменять элементы. Поскольку у нас есть индекс, мы можем присвоить новое значение любому элементу массива.

Пример: Увеличим каждый элемент массива на 5:

int[] scores = {10, 15, 20, 25, 30};

for (int i = 0; i < scores.length; i++) {
    scores[i] = scores[i] + 5;
}

for (int i = 0; i < scores.length; i++) {
    System.out.println(scores[i]);
}

Вывод:

15
20
25
30
35

Пример: Заменим все отрицательные числа на ноль:

int[] values = {-3, 5, -1, 8, -7, 2};

for (int i = 0; i < values.length; i++) {
    if (values[i] < 0) {
        values[i] = 0;
    }
}

for (int i = 0; i < values.length; i++) {
    System.out.print(values[i] + " ");
}
System.out.println();
// Вывод: 0 5 0 8 0 2

Пример: Заполним массив квадратами чисел от 1 до 5:

int[] squares = new int[5];

for (int i = 0; i < squares.length; i++) {
    squares[i] = (i + 1) * (i + 1);
}

for (int i = 0; i < squares.length; i++) {
    System.out.println((i + 1) + "^2 = " + squares[i]);
}
// Вывод:
// 1^2 = 1
// 2^2 = 4
// 3^2 = 9
// 4^2 = 16
// 5^2 = 25

Совет: Обратите внимание, что i — это не только индекс, но и число. Когда нам нужно вычислить квадрат числа, начинающегося с 1, а индексы начинаются с 0, мы используем выражение i + 1 для получения «человеческого» номера. Это очень распространённый приём.

Обратный перебор массива

Иногда нужно пройти массив не от начала к концу, а наоборот — от последнего элемента к первому. Это полезно, например, когда вы хотите распечатать список в обратном порядке или удалять элементы с конца.

Для обратного перебора нужно начать с индекса length - 1 и уменьшать индекс до 0 включительно:

int[] numbers = {10, 20, 30, 40, 50};

for (int i = numbers.length - 1; i >= 0; i--) {
    System.out.println("Элемент [" + i + "] = " + numbers[i]);
}

Вывод:

Элемент [4] = 50
Элемент [3] = 40
Элемент [2] = 30
Элемент [1] = 20
Элемент [0] = 10

Разберём ключевые отличия от прямого обхода:

  • Начальное значение: i = numbers.length - 1 (последний индекс)
  • Условие: i >= 0 (продолжаем, пока i не станет меньше 0)
  • Шаг: i-- (уменьшаем i на 1)

Ошибка новичков: Часто забывают условие >= 0 и пишут > 0. В этом случае последний элемент (с индексом 0) будет пропущен! Всегда проверяйте: если вы начинаете с 0, то условие должно включать 0.

Пример: Выведем массив строк в обратном порядке — например, историю действий пользователя:

String[] history = {"Открыл файл", "Написал код", "Сохранил", "Закрыл"};

for (int i = history.length - 1; i >= 0; i--) {
    System.out.println((history.length - i) + ". " + history[i]);
}
// Вывод:
// 1. Закрыл
// 2. Сохранил
// 3. Написал код
// 4. Открыл файл

Обратный перебор также полезен, когда нужно вычислить сумму или произведение элементов «от конца». Логика идентична прямому обходу, меняются только начальное значение и шаг:

int[] prices = {100, 200, 300, 400, 500};
int sum = 0;

for (int i = prices.length - 1; i >= 0; i--) {
    sum += prices[i];
}
System.out.println("Сумма: " + sum);  // Сумма: 1500

Произвольный шаг цикла

Классический цикл for позволяет задавать произвольный шаг. Это открывает интересные возможности: можно перебирать только чётные индексы, пропускать элементы через один, или двигаться с шагом 2.

Пример: Выведем только элементы с чётными индексами (0, 2, 4):

int[] data = {10, 20, 30, 40, 50, 60, 70};

for (int i = 0; i < data.length; i += 2) {
    System.out.println("data[" + i + "] = " + data[i]);
}
// Вывод:
// data[0] = 10
// data[2] = 30
// data[4] = 50
// data[6] = 70

Пример: Выведем каждый третий элемент:

int[] sequence = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for (int i = 0; i < sequence.length; i += 3) {
    System.out.print(sequence[i] + " ");
}
System.out.println();
// Вывод: 1 4 7 10

Шаг может быть любым целым числом — положительным или отрицательным. Главное — убедиться, что цикл завершится и не зациклится.

Осторожно: Если ваш шаг нечётный, а условие не покрывает все индексы, вы можете пропустить последний элемент. Например, при i += 2 и массиве длины 5, последний обработанный индекс будет 4 (что корректно), но при длине 4 — последний обработанный индекс будет 2, и элемент с индексом 3 будет пропущен.

Цикл for-each (усечённый for)

Цикл for-each — это более простой и читаемый способ перебора всех элементов массива. Он был добавлен в Java 5 и сразу стал очень популярен, потому что избавляет от необходимости управлять индексом вручную.

Синтаксис:

for (ТипЭлемента имяПеременной : массив) {
    // здесь имяПеременной — это текущий элемент
}

Знак : читается как «в». То есть for (int num : numbers) читается как «для каждого числа num в массиве numbers».

Пример: Выведем все элементы массива чисел:

int[] numbers = {10, 20, 30, 40, 50};

for (int num : numbers) {
    System.out.println(num);
}
// Вывод:
// 10
// 20
// 30
// 40
// 50

Сравните две записи — классический for и for-each:

// Классический for:
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

// For-each:
for (int num : numbers) {
    System.out.println(num);
}

Оба цикла делают одно и то же, но for-each короче и нагляднее. Когда индекс не нужен — используйте for-each.

Пример: Выведем имена студентов:

String[] students = {"Анна", "Борис", "Виктор", "Галина"};

for (String name : students) {
    System.out.println("Студент: " + name);
}
// Вывод:
// Студент: Анна
// Студент: Борис
// Студент: Виктор
// Студент: Галина

Пример: Посчитаем сумму чисел с помощью for-each:

int[] prices = {100, 250, 300, 150, 400};
int total = 0;

for (int price : prices) {
    total += price;
}

System.out.println("Общая сумма: " + total);  // Общая сумма: 1200

Совет: Используйте for-each, когда вам нужно просто пройтись по всем элементам и прочитать их. Он короче, чище и менее подвержен ошибкам (нет риска выйти за границы массива).

Когда for-each не подходит

For-each выглядит элегантно, но он имеет серьёзные ограничения. Давайте разберём ситуации, когда его использовать нельзя или неудобно.

1. Нужно изменить элементы массива

Переменная в for-each — это копия текущего элемента, а не ссылка на него. Поэтому присваивание новой значения этой переменной не изменяет массив:

int[] numbers = {10, 20, 30};

// ❌ НЕ ПОЛУЧИТСЯ изменить массив:
for (int num : numbers) {
    num = num * 2;  // это изменяет только локальную переменную num!
}

System.out.println(numbers[0]);  // 10 — массив НЕ изменился!

Чтобы изменить элементы, нужен классический for с индексом:

int[] numbers = {10, 20, 30};

// ✅ ПРАВИЛЬНО — через индекс:
for (int i = 0; i < numbers.length; i++) {
    numbers[i] = numbers[i] * 2;
}

System.out.println(numbers[0]);  // 20 — массив изменён!

2. Нужен индекс текущего элемента

В for-each нет переменной с индексом. Если вам нужно знать номер позиции (например, для вывода «Элемент 3: значение»), for-each не подходит:

String[] fruits = {"Яблоко", "Банан", "Вишня", "Груша"};

// ❌ Без индекса не получится красиво пронумеровать:
for (String fruit : fruits) {
    // Как вывести номер? Невозможно!
}

// ✅ Нужен классический for:
for (int i = 0; i < fruits.length; i++) {
    System.out.println((i + 1) + ". " + fruits[i]);
}
// Вывод:
// 1. Яблоко
// 2. Банан
// 3. Вишня
// 4. Груша

3. Нужно работать с двумя соседними элементами

Если задача требует сравнения соседних элементов (например, найти пару максимальных соседних чисел), for-each не поможет:

int[] data = {5, 12, 8, 20, 3, 15};

// Найти максимальную сумму двух соседних элементов
int maxSum = data[0] + data[1];

for (int i = 1; i < data.length - 1; i++) {
    int pairSum = data[i] + data[i + 1];
    if (pairSum > maxSum) {
        maxSum = pairSum;
    }
}

System.out.println("Максимальная сумма соседних: " + maxSum);  // 28 (12+16 нет... 20+3=23... 12+8=20)
// Правильно: 12+8=20, 8+20=28, 20+3=23, 3+15=18 → ответ 28

4. Нужен обратный обход

For-each всегда идёт от первого элемента к последнему. Обратный обход возможен только через классический for:

int[] stack = {1, 2, 3, 4, 5};

// ❌ For-each всегда идёт от начала:
for (int item : stack) {
    System.out.print(item + " ");  // 1 2 3 4 5
}

System.out.println();

// ✅ Обратный обход через for:
for (int i = stack.length - 1; i >= 0; i--) {
    System.out.print(stack[i] + " ");  // 5 4 3 2 1
}

5. Нужно изменить размер массива (удалить/вставить)

Хотя в Java массивы фиксированного размера, иногда эмулируют удаление элемента, сдвигая остальные. Для этого нужен индекс:

int[] arr = {10, 20, 30, 40, 50};
int removeIndex = 2;  // удаляем элемент с индексом 2 (значение 30)

for (int i = removeIndex; i < arr.length - 1; i++) {
    arr[i] = arr[i + 1];
}

for (int i = 0; i < arr.length - 1; i++) {
    System.out.print(arr[i] + " ");
}
// Вывод: 10 20 40 50

Правило: Используйте for-each когда вам нужно только ЧИТАТЬ элементы. Используйте классический for когда вам нужен ИНДЕКС или когда вы ИЗМЕНЯЕТЕ массив.

Сравнение: for vs for-each

Давайте подведём промежуточный итог и составим таблицу сравнения двух подходов:

Возможность for (с индексом) for-each
Чтение всех элементов Да Да (лучше!)
Изменение элементов Да Нет
Доступ к индексу Да Нет
Обратный обход Да Нет
Произвольный шаг Да Нет
Читаемость кода Средняя Высокая
Риск ошибки выхода за границы Есть Нет

Золотое правило: Если вам нужен только для чтения — используйте for-each. Если нужен индекс, изменение или обратный обход — классический for. В реальных проектах оба типа используются одинаково часто.

Заполнение массивов циклами

Одна из самых частых задач — заполнить массив какими-то значениями, используя формулу или закономерность. Цикл for идеально подходит для этого, потому что индекс и значение связаны формулой.

Пример: Заполним массив числами от 1 до 10:

int[] numbers = new int[10];

for (int i = 0; i < numbers.length; i++) {
    numbers[i] = i + 1;
}

for (int num : numbers) {
    System.out.print(num + " ");
}
System.out.println();
// Вывод: 1 2 3 4 5 6 7 8 9 10

Пример: Заполним массив чётными числами (2, 4, 6, 8, ...):

int[] evens = new int[10];

for (int i = 0; i < evens.length; i++) {
    evens[i] = (i + 1) * 2;
}

for (int num : evens) {
    System.out.print(num + " ");
}
System.out.println();
// Вывод: 2 4 6 8 10 12 14 16 18 20

Пример: Заполним массив степенями двойки (1, 2, 4, 8, 16, ...):

int[] powers = new int[8];

for (int i = 0; i < powers.length; i++) {
    powers[i] = (int) Math.pow(2, i);
}

for (int p : powers) {
    System.out.print(p + " ");
}
System.out.println();
// Вывод: 1 2 4 8 16 32 64 128

Пример: Заполним строковый массив днями недели:

String[] days = new String[7];

days[0] = "Понедельник";
days[1] = "Вторник";
days[2] = "Среда";
days[3] = "Четверг";
days[4] = "Пятница";
days[5] = "Суббота";
days[6] = "Воскресенье";

for (String day : days) {
    System.out.println(day);
}

Пример: Заполним массив случайными числами от 1 до 100:

int[] randoms = new int[5];

for (int i = 0; i < randoms.length; i++) {
    randoms[i] = (int) (Math.random() * 100) + 1;
}

for (int r : randoms) {
    System.out.print(r + " ");
}
System.out.println();
// Вывод (например): 42 87 13 95 61

Копирование массивов

В Java при присваивании массива другой переменной НЕ создаётся копия. Обе переменные ссылаются на один и тот же объект в памяти. Это ловушка, в которую попадают все новички:

int[] original = {1, 2, 3};
int[] copy = original;  // ❌ Это НЕ копия, а та же ссылка!

copy[0] = 999;

System.out.println(original[0]);  // 999! original тоже изменился!

Чтобы создать настоящую независимую копию, нужно скопировать каждый элемент вручную через цикл:

int[] original = {1, 2, 3, 4, 5};
int[] copy = new int[original.length];

for (int i = 0; i < original.length; i++) {
    copy[i] = original[i];
}

copy[0] = 999;

System.out.println(original[0]);  // 1 — original НЕ изменился!
System.out.println(copy[0]);      // 999

Давайте разберём, что происходит в памяти. После присваивания int[] copy = original обе переменные указывают на один объект:

original ——→ [1, 2, 3, 4, 5]
copy ———————↗  (та же область памяти)

После правильного копирования через цикл создаётся новый объект:

original ——→ [1, 2, 3, 4, 5]
copy ——→ [1, 2, 3, 4, 5]     (другая область памяти)

Пример: Скопируем массив строк:

String[] fruits = {"Яблоко", "Банан", "Вишня"};
String[] fruitsCopy = new String[fruits.length];

for (int i = 0; i < fruits.length; i++) {
    fruitsCopy[i] = fruits[i];
}

fruitsCopy[1] = "Манго";

System.out.println(fruits[1]);       // Банан
System.out.println(fruitsCopy[1]);   // Манго

Осторожно: При копировании ссылочных типов (String, объекты) копируются ССЫЛКИ, а не сами объекты. Если элементы массива — объекты, то копия массива будет указывать на те же самые объекты. Для полной независимости нужно клонировать и сами объекты.

Копирование массива в обратном порядке (если нужно перевернуть):

int[] original = {1, 2, 3, 4, 5};
int[] reversed = new int[original.length];

for (int i = 0; i < original.length; i++) {
    reversed[i] = original[original.length - 1 - i];
}

for (int num : reversed) {
    System.out.print(num + " ");
}
System.out.println();
// Вывод: 5 4 3 2 1

Копирование части массива — когда нужно взять только часть элементов:

int[] source = {10, 20, 30, 40, 50};
int[] partial = new int[3];

for (int i = 0; i < 3; i++) {
    partial[i] = source[i + 1];
}

for (int num : partial) {
    System.out.print(num + " ");
}
System.out.println();
// Вывод: 20 30 40

Arrays.toString() — красивый вывод массива

Если вы попробуете вывести массив напрямую с помощью System.out.println(array), вы увидите не значения элементов, а что-то вроде [I@15db9742 — это внутреннее представление объекта. Это не полезно для отладки.

int[] numbers = {10, 20, 30};
System.out.println(numbers);
// Вывод: [I@15db9742  (непонятно!)

Чтобы красиво вывести содержимое массива, используется статический метод toString() из класса java.util.Arrays:

import java.util.Arrays;

int[] numbers = {10, 20, 30, 40, 50};
System.out.println(Arrays.toString(numbers));
// Вывод: [10, 20, 30, 40, 50]

Это гораздо удобнее! Метод Arrays.toString() принимает массив любого типа и возвращает строку с элементами, разделёнными запятыми, в квадратных скобках:

import java.util.Arrays;

String[] colors = {"красный", "зелёный", "синий"};
System.out.println(Arrays.toString(colors));
// Вывод: [красный, зелёный, синий]

double[] prices = {9.99, 19.99, 29.99};
System.out.println(Arrays.toString(prices));
// Вывод: [9.99, 19.99, 29.99]

boolean[] flags = {true, false, true, true};
System.out.println(Arrays.toString(flags));
// Вывод: [true, false, true, true]

Обратите внимание на импорт: import java.util.Arrays; должен быть в начале файла. Без него компилятор не найдёт класс Arrays.

Пример: Выведем массив до и после заполнения:

import java.util.Arrays;

int[] data = new int[5];
System.out.println("До заполнения: " + Arrays.toString(data));

for (int i = 0; i < data.length; i++) {
    data[i] = (i + 1) * 10;
}

System.out.println("После заполнения: " + Arrays.toString(data));
// Вывод:
// До заполнения: [0, 0, 0, 0, 0]
// После заполнения: [10, 20, 30, 40, 50]

Совет: Используйте Arrays.toString() при отладке — это самый быстрый способ проверить содержимое массива. В следующем уроке мы детально изучим все методы класса Arrays.

Для двумерных массивов нужен другой метод — Arrays.deepToString():

import java.util.Arrays;

int[][] matrix = {{1, 2, 3}, {4, 5, 6}};
System.out.println(Arrays.toString(matrix));      // [[I@15db9742, [I@6d06d69c]
System.out.println(Arrays.deepToString(matrix));  // [[1, 2, 3], [4, 5, 6]]

Об этом методе мы подробно поговорим в уроке о многомерных массивах.

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

Давайте закрепим знания на реальных задачах. Каждый пример решается двумя способами — через for и через for-each — чтобы вы могли выбрать подходящий.

Задача 1: Найти сумму всех элементов

import java.util.Arrays;

int[] grades = {5, 4, 3, 5, 4, 5, 4};

// Способ 1: for-each (индекс не нужен)
int sum = 0;
for (int grade : grades) {
    sum += grade;
}
System.out.println("Сумма оценок: " + sum);

// Способ 2: классический for
int sum2 = 0;
for (int i = 0; i < grades.length; i++) {
    sum2 += grades[i];
}
System.out.println("Сумма оценок: " + sum2);

Задача 2: Найти среднее значение

double[] temperatures = {23.5, 25.1, 22.8, 24.3, 26.0};

double sum = 0;
for (double temp : temperatures) {
    sum += temp;
}
double average = sum / temperatures.length;

System.out.println("Средняя температура: " + average);
// Вывод: Средняя температура: 24.34

Задача 3: Найти максимум

int[] heights = {170, 185, 162, 190, 175, 168};

int max = heights[0];
for (int i = 1; i < heights.length; i++) {
    if (heights[i] > max) {
        max = heights[i];
    }
}
System.out.println("Максимальный рост: " + max);  // 190

Обратите внимание: начальное значение max — это первый элемент массива, а не 0. Если все числа отрицательные и начать с 0, результат будет неверным!

Задача 4: Найти минимум

int[] prices = {500, 120, 350, 80, 450, 200};

int min = prices[0];
for (int i = 1; i < prices.length; i++) {
    if (prices[i] < min) {
        min = prices[i];
    }
}
System.out.println("Минимальная цена: " + min);  // 80

Задача 5: Подсчитать количество чётных чисел

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int evenCount = 0;

for (int num : numbers) {
    if (num % 2 == 0) {
        evenCount++;
    }
}

System.out.println("Количество чётных: " + evenCount);  // 5

Задача 6: Поиск элемента в массиве

String[] fruits = {"Яблоко", "Банан", "Вишня", "Груша", "Манго"};
String search = "Вишня";
int foundIndex = -1;

for (int i = 0; i < fruits.length; i++) {
    if (fruits[i].equals(search)) {
        foundIndex = i;
        break;
    }
}

if (foundIndex != -1) {
    System.out.println("Найдено: " + search + " на позиции " + foundIndex);
} else {
    System.out.println(search + " не найдено в массиве");
}
// Вывод: Найдено: Вишня на позиции 2

Задача 7: Инвертирование массива (переворот)

int[] original = {1, 2, 3, 4, 5};
int[] reversed = new int[original.length];

for (int i = 0; i < original.length; i++) {
    reversed[i] = original[original.length - 1 - i];
}

System.out.println("Оригинал: " + Arrays.toString(original));
System.out.println("Реверс:   " + Arrays.toString(reversed));
// Вывод:
// Оригинал: [1, 2, 3, 4, 5]
// Реверс:   [5, 4, 3, 2, 1]

Задача 8: Подсчитать количество положительных, отрицательных и нулевых

int[] values = {-3, 0, 5, -1, 0, 8, -7, 2, 0};
int positive = 0;
int negative = 0;
int zeroes = 0;

for (int val : values) {
    if (val > 0) {
        positive++;
    } else if (val < 0) {
        negative++;
    } else {
        zeroes++;
    }
}

System.out.println("Положительных: " + positive);   // 3
System.out.println("Отрицательных: " + negative);   // 3
System.out.println("Нулевых: " + zeroes);           // 3

Задача 9: Создать копию массива в обратном порядке

char[] original = {'A', 'B', 'C', 'D', 'E'};
char[] reversed = new char[original.length];

for (int i = 0; i < original.length; i++) {
    reversed[original.length - 1 - i] = original[i];
}

System.out.println("Оригинал: " + Arrays.toString(original));
System.out.println("Реверс:   " + Arrays.toString(reversed));
// Вывод:
// Оригинал: [A, B, C, D, E]
// Реверс:   [E, D, C, B, A]

Типичные ошибки и ловушки

При работе с перебором массивов есть несколько ошибок, которые совершают практически все начинающие программисты. Разберём каждую подробно.

Ошибка 1: Выход за границы массива (ArrayIndexOutOfBoundsException)

int[] arr = {1, 2, 3, 4, 5};

// ❌ Ошибка: i <= length вместо i < length
for (int i = 0; i <= arr.length; i++) {
    System.out.println(arr[i]);  // ArrayIndexOutOfBoundsException при i=5!
}

Причина: массив длины 5 имеет индексы от 0 до 4. Индекс 5 не существует. Условие <= позволяет циклу дойти до 5, что вызывает ошибку.

Запомните: Для массивов длины N индексы от 0 до N-1. Цикл должен быть i < arr.length (строго меньше), а не i <= arr.length.

Ошибка 2: Зацикливание

int[] arr = {1, 2, 3};

// ❌ Забыли увеличить i
for (int i = 0; i < arr.length; ) {
    System.out.println(arr[i]);  // Бесконечный цикл! i всегда 0
}

Ошибка 3: Неправильная инициализация max/min

int[] arr = {-5, -3, -10, -2};

// ❌ Неправильно: max = 0, хотя все числа отрицательные
int max = 0;
for (int num : arr) {
    if (num > max) {
        max = num;
    }
}
System.out.println(max);  // 0 — неверно! Правильный ответ: -2

// ✅ Правильно: max = первый элемент
int maxCorrect = arr[0];
for (int num : arr) {
    if (num > maxCorrect) {
        maxCorrect = num;
    }
}
System.out.println(maxCorrect);  // -2 — верно!

Ошибка 4: Путаница между for и for-each при изменении

int[] arr = {1, 2, 3};

// ❌ Пытаемся изменить через for-each — не работает
for (int num : arr) {
    num *= 2;
}
System.out.println(Arrays.toString(arr));  // [1, 2, 3] — не изменилось!

// ✅ Правильно через for
for (int i = 0; i < arr.length; i++) {
    arr[i] *= 2;
}
System.out.println(Arrays.toString(arr));  // [2, 4, 6]

Ошибка 5: Вывод массива без Arrays.toString()

int[] arr = {1, 2, 3};

// ❌ Прямой вывод — непонятный адрес объекта
System.out.println(arr);  // [I@15db9742

// ✅ Используем Arrays.toString()
System.out.println(Arrays.toString(arr));  // [1, 2, 3]

Совмещение циклов: вложенные переборы

Иногда нужно для каждого элемента одного массива пройтись по всем элементам другого. Это решается вложенными циклами. Например, сравнение всех пар элементов:

int[] a = {1, 2, 3};
int[] b = {4, 5, 6};

for (int i = 0; i < a.length; i++) {
    for (int j = 0; j < b.length; j++) {
        System.out.println(a[i] + " + " + b[j] + " = " + (a[i] + b[j]));
    }
}
// Вывод:
// 1 + 4 = 5
// 1 + 5 = 6
// 1 + 6 = 7
// 2 + 4 = 6
// 2 + 5 = 7
// 2 + 6 = 8
// 3 + 4 = 7
// 3 + 5 = 8
// 3 + 6 = 9

Это основа для работы с многомерными массивами, о которых мы поговорим в следующих уроках.

Итоги урока

  • Классический цикл for с индексом — универсальный способ перебора массива
  • Цикл for-each — короткая запись для перебора без доступа к индексу
  • For-each идеален для чтения всех элементов, но не подходит для изменения массива
  • Для изменения элементов, обратного обхода и работы с индексом используйте классический for
  • Массивы копируются элемент за элементом через цикл — присваивание создаёт только ссылку
  • Arrays.toString() красиво выводит содержимое массива на экран
  • Всегда проверяйте границы: индексы от 0 до length-1, условие i < length
  • Для поиска максимума/минимума инициализируйте переменную первым элементом массива

Тест: Перебор массивов — цикл for и for-each

8 вопросов

Поиск максимума

Premium