Перебор массивов: цикл for и for-each
Научимся проходить по каждому элементу массива разными способами, заполнять, копировать и выводить массивы
Зачем перебирать массивы?
Представьте, что учитель проверяет тетради 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 - Для поиска максимума/минимума инициализируйте переменную первым элементом массива
Следующий урок: Методы для работы с массивами: java.util.Arrays
Тест: Перебор массивов — цикл for и for-each
8 вопросов