Циклы в Java: синтаксис, примеры и выбор конструкции

Цикл for java удобен для диапазонов и индексов, а также повторяет код по условию. Foreach перебирает элементы последовательно, while повторяет тело до события, а do-while запускает его хотя бы один раз. В статье разберем циклы в java, примеры вывода и ошибки, которые мешают компиляции.

Какие циклы есть в Java

Java предлагает 4 основные конструкции: for, расширенный for (foreach), while и do-while. Выбирайте конструкцию по границе повторов, доступу к индексу и моменту проверки условия.

ЦиклСинтаксисКогда проверяется условиеМинимум итерацийДоступ к индексуТипичная задача
forfor (инициализация; условие; шаг)До тела0Да, счетчик цикла служит индексомДиапазон чисел или массив
foreachfor (Тип элемент : коллекция)До тела0Встроенного индекса нетВсе элементы массива или коллекции
whilewhile (условие)До тела0Зависит от кодаПовтор до события
do-whiledo { ... } while (условие);После тела1Зависит от кодаМеню или повторный ввод

Для запуска примеров установите JDK с официальной страницы Java. Код удобно редактировать в IntelliJ IDEA Community или текстовом редакторе. Имя файла должно совпадать с именем public-класса.

Сохраните пример как ForOrder.java, выполните javac ForOrder.java, затем запустите java ForOrder. Правила конструкций описывает глава 14 JLS: разделы 14.12–14.14 посвящены циклам, 14.7 – меткам, 14.15–14.16 – break и continue.

Цикл for в Java: синтаксис и порядок выполнения

Цикл for java объединяет счетчик, проверку и изменение счетчика в одной строке. Он подходит, когда границы или шаг повторения известны заранее.

Три части заголовка

Java выполняет инициализацию один раз. Затем цикл проверяет условие, запускает тело и выполняет обновление счетчика.

public class ForOrder {
    public static void main(String[] args) {
        for (int i = 1; i <= 3; i++) {
            System.out.println(i);
        }
    }
}

Вывод:

1
2
3

После обновления управление возвращается к условию. Если оно дает false, управление переходит к инструкции после цикла.

Область видимости счетчика

Счетчик, объявленный в заголовке, виден в условии, обновлении и теле цикла. После цикла имя недоступно, потому что область переменной закончилась.

public class ForScope {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            System.out.println(i);
        }
        // System.out.println(i); // ошибка: cannot find symbol
    }
}

Закомментированная строка не мешает компиляции. Если убрать //, компилятор сообщит cannot find symbol. Когда значение нужно после цикла, объявите переменную заранее.

public class ForOutsideCounter {
    public static void main(String[] args) {
        int i;
        for (i = 1; i <= 3; i++) {
            System.out.println(i);
        }
        System.out.println("После цикла: " + i);
    }
}

Вывод:

1
2
3
После цикла: 4

В заголовке цикла можно использовать var: такая форма доступна с Java 10 и требует начального значения.

for (var i = 0; i < 3; i++) {
    System.out.println(i);
}

Переменные, пропущенные части и шаг

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

public class ForParts {
    public static void main(String[] args) {
        for (int left = 1, right = 5; left <= right; left += 2, right++) {
            System.out.println(left + " " + right);
        }
    }
}

Вывод:

1 5
3 6
5 7
7 8
9 9

Инициализацию и обновление можно вынести из заголовка, но точки с запятой сохраняются.

public class ForVariants {
    public static void main(String[] args) {
        int i = 3;
        for (; i > 0;) {
            System.out.println(i);
            i--;
        }
    }
}

Вывод:

3
2
1

Для обратного отсчета уменьшайте счетчик. Шаг больше 1 задают через i += 2 или i -= 2. Конструкция for (;;) не проверяет условие и требует выхода через break, return или исключение.

public class ForStep {
    public static void main(String[] args) {
        for (int i = 10; i >= 0; i -= 2) {
            System.out.print(i + " ");
        }
    }
}

Вывод:

10 8 6 4 2 0 

Метод System.out.println добавляет перевод строки, а System.out.print оставляет вывод в текущей строке. Пробел между числами в примере добавляет выражение i + " ".

Цикл foreach в Java для массивов и коллекций

Цикл foreach java записывают как for (Тип элемент : коллекция). В Java нет отдельного ключевого слова foreach: это разговорное название расширенного for. Конструкция работает с массивами и объектами Iterable.

Массив и ArrayList

Переменная цикла получает значение очередного элемента. Присваивание переменной не заменяет значение в массиве.

public class ForeachArray {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30};
        for (int number : numbers) {
            System.out.println(number);
        }
    }
}

Вывод:

10
20
30

Для изменения элементов используйте индексный for. Если массив хранит изменяемые объекты, вызов метода через переменную цикла может изменить состояние самого объекта.

public class ForeachList {
    public static void main(String[] args) {
        java.util.ArrayList<String> names = new java.util.ArrayList<>();
        names.add("Анна");
        names.add("Олег");
        for (String name : names) {
            System.out.println(name);
        }
    }
}

Вывод:

Анна
Олег

Индексный обход и двумерный массив

Берите обычный for, когда нужен индекс, обратный проход или часть списка. У ArrayList метод size() возвращает размер, а get(i) получает элемент.

public class ForArrayList {
    public static void main(String[] args) {
        java.util.ArrayList<String> names = new java.util.ArrayList<>();
        names.add("Анна");
        names.add("Олег");
        for (int i = 0; i < names.size(); i++) {
            System.out.println(i + ": " + names.get(i));
        }
    }
}

Вывод:

0: Анна
1: Олег

Двумерный массив хранит массивы строк. Внешний foreach получает строку, внутренний – ее элементы.

public class ForeachMatrix {
    public static void main(String[] args) {
        int[][] matrix = {{1, 2}, {3, 4}};
        for (int[] row : matrix) {
            for (int value : row) {
                System.out.print(value + " ");
            }
            System.out.println();
        }
    }
}

Вывод:

1 2 
3 4 

Удаление элемента во время foreach

Прямое удаление из ArrayList во время foreach нарушает работу итератора. При следующем обращении к нему обычно возникает ConcurrentModificationException.

Для удаления во время обхода используйте Iterator.remove().

public class IteratorRemove {
    public static void main(String[] args) {
        java.util.ArrayList<Integer> numbers = new java.util.ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        java.util.Iterator<Integer> iterator = numbers.iterator();
        while (iterator.hasNext()) {
            if (iterator.next() % 2 == 0) {
                iterator.remove();
            }
        }
        System.out.println(numbers);
    }
}

Вывод:

[1, 3]

Для удаления по условию подходит removeIf.

public class RemoveIfExample {
    public static void main(String[] args) {
        java.util.ArrayList<Integer> numbers = new java.util.ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.removeIf(number -> number % 2 == 0);
        System.out.println(numbers);
    }
}

Вывод:

[1, 3]

Цикл while в Java

while проверяет условие перед каждой итерацией. Если оно сразу ложно, тело не запускается. Используйте этот цикл, когда число повторов зависит от ввода или изменяемого состояния.

public class WhileDigits {
    public static void main(String[] args) {
        int number = 482;
        int digits = 0;
        while (number > 0) {
            digits++;
            number /= 10;
        }
        System.out.println(digits);
    }
}

Вывод:

3

Каждая итерация удаляет последнюю цифру целочисленным делением на 10. Для числа 0 нужна отдельная обработка, потому что запись этого числа содержит одну цифру.

public class WhileInput {
    public static void main(String[] args) {
        java.util.Scanner scanner = new java.util.Scanner(System.in);
        String text;
        while (scanner.hasNextLine() && !(text = scanner.nextLine()).isEmpty()) {
            System.out.println("Введено: " + text);
        }
        scanner.close();
    }
}

При вводе строк один, два и пустой строки вывод будет таким:

Введено: один
Введено: два

Цикл do-while и обязательная первая итерация

do-while сначала запускает тело, затем проверяет условие. Поэтому такая конструкция выполняется минимум 1 раз. После условия ставится точка с запятой.

public class DoWhileExample {
    public static void main(String[] args) {
        int choice = 0;
        do {
            System.out.println("Меню показано");
            choice++;
        } while (choice < 1);
    }
}

Вывод:

Меню показано

Берите do-while для меню или ввода, который нужно запросить хотя бы один раз. В отличие от while, эта конструкция выполняет тело до первой проверки.

Вложенные циклы и двумерные данные

Вложенный цикл запускается для каждой итерации внешнего. Если внешний цикл выполняется 3 раза, а внутренний – 4 раза, внутреннее тело сработает 12 раз.

public class MultiplicationTable {
    public static void main(String[] args) {
        for (int row = 1; row <= 2; row++) {
            for (int column = 1; column <= 3; column++) {
                System.out.print(row * column + " ");
            }
            System.out.println();
        }
    }
}

Вывод:

1 2 3 
2 4 6 

Внутреннее тело выполняется 6 раз. Для прямоугольной матрицы число повторов равно произведению числа строк и столбцов. Если строки имеют разную длину, сложите количество элементов всех строк.

break, continue и метки в Java

Оператор break без метки завершает ближайший цикл. Оператор continue пропускает остаток текущей итерации и передает управление следующему повтору.

  • В for после continue выполняется обновление счетчика.
  • В while управление возвращается к проверке условия.
  • В do-while управление переходит к проверке после тела.
  • В foreach цикл получает следующий элемент, если он существует.
public class BreakContinue {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            if (i == 2) {
                continue;
            }
            if (i == 5) {
                break;
            }
            System.out.print(i + " ");
        }
    }
}

Вывод:

1 3 4 

Метка перед внешним циклом позволяет выйти сразу из нескольких уровней.

public class LabeledBreak {
    public static void main(String[] args) {
        int[][] matrix = {{1, 2}, {3, 4}};
        search:
        for (int row = 0; row < matrix.length; row++) {
            for (int column = 0; column < matrix[row].length; column++) {
                if (matrix[row][column] == 3) {
                    System.out.println("Найдено");
                    break search;
                }
            }
        }
    }
}

Вывод:

Найдено

Оператор break search завершает помеченный внешний цикл. Запись continue search продолжила бы внешний цикл с обновлением row++. Метки используйте только там, где без них вложенность трудно завершить.

Метод forEach и Stream API

Метод forEach выполняет действие для каждого элемента Iterable. Он доступен начиная с Java 8 и подходит для короткой операции без ручного счетчика.

public class ForEachMethod {
    public static void main(String[] args) {
        java.util.List<String> words = new java.util.ArrayList<>();
        words.add("Java");
        words.add("цикл");
        words.forEach(word -> System.out.println(word));
    }
}

Вывод:

Java
цикл

Для диапазона чисел можно применить IntStream.range. Верхняя граница в диапазон не входит.

public class IntStreamExample {
    public static void main(String[] args) {
        java.util.stream.IntStream.range(1, 4)
                .forEach(i -> System.out.print(i + " "));
    }
}

Вывод:

1 2 3 

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

Ошибки в циклах Java

Ошибки чаще всего появляются из-за неверной границы, неизменного счетчика или изменения коллекции во время обхода. Сначала найдите выражение, которое должно менять состояние программы.

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

Индексы массива идут от 0 до length - 1. Условие i <= array.length допускает лишний индекс и вызывает ArrayIndexOutOfBoundsException.

public class ArrayBounds {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30};
        for (int i = 0; i < numbers.length; i++) {
            System.out.println(numbers[i]);
        }
    }
}

Вывод:

10
20
30

Используйте оператор < и поле length. Такая граница подходит массиву любого размера.

Бесконечный цикл и точка с запятой

Если while проверяет неизменный счетчик, условие остается истинным. Добавьте изменение счетчика на пути каждой обычной итерации.

public class InfiniteLoopFix {
    public static void main(String[] args) {
        int i = 0;
        while (i < 3) {
            System.out.println(i);
            i++;
        }
    }
}

Вывод:

0
1
2

В обычном for обновление выполняется после continue. В while увеличение счетчика нужно разместить так, чтобы continue его не пропускал.

public class EmptyLoopBody {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++); {
            System.out.println("Блок вне цикла");
        }
    }
}

Вывод:

Блок вне цикла

Точка с запятой завершает цикл с пустым телом. Уберите ее, если блок должен выполняться на каждой итерации.

Коллекция и сравнение double

Удаляйте элементы ArrayList через Iterator.remove() или removeIf. Прямое удаление списка внутри foreach может вызвать ConcurrentModificationException.

Не используйте точное равенство накопленного double как единственную границу. Целочисленный счетчик задает число шагов, а дробное значение вычисляется внутри.

public class DoubleLoopFix {
    public static void main(String[] args) {
        double value = 0.0;
        for (int step = 0; step < 10; step++) {
            value += 0.1;
        }
        System.out.println(value > 0.9);
    }
}

Вывод:

true

Цикл завершается по целому числу итераций. Для точных десятичных расчетов используйте BigDecimal.

Какой цикл выбрать для задачи

Сопоставьте задачу с границей повторов и данными. Такой выбор уменьшает число вспомогательных переменных и снижает риск ошибки индекса.

ЗадачаПодходящий циклПричина
Повторить код для чисел от 0 до границыforСчетчик и шаг находятся в заголовке
Получить индекс или заменить элемент массиваforСчетчик доступен в теле
Пройти все элементы массива или коллекцииforeachРучной индекс не нужен
Повторять до получения значенияwhileУсловие проверяется перед телом
Показать меню хотя бы один разdo-whileТело выполняется до проверки
Выполнить короткое действие для коллекцииforEachДействие передается в лямбде

Берите for для диапазона и индексов. Выбирайте foreach для последовательного чтения элементов. Используйте while при неизвестном числе повторов, а do-while – при обязательном первом запуске.

Совет от нашей редакции: сначала сформулируйте условие остановки словами, затем выберите цикл, который выражает его без лишнего состояния.

Комментарии

Оставьте свой комментарий
Пока никто не оставил комментариев