Инкремент и декремент в Java
Что за операторы такие, зачем им префиксная и постфиксная формы, и как вычислять сложные выражения с ними (пригодится на экзамене и собеседовании).
В программировании часто приходится выполнять операции (вычислять результат выражений), в которых переменная должна увеличиться или уменьшиться на единицу.
Для этого придуманы унарные операторы ++ и --. С ними код короче и писать его быстрее.
Оператор (++) называется инкремент. Он увеличивает значение целочисленной переменной на единицу.
Обратный оператор (--) зовётся декремент. Он значение переменной на один уменьшает.
Пример 1
Тут всё просто, достаточно удостовериться, что значения переменных поменялись.
Примечание. Инкремент и декремент относятся к арифметическим операторам. Мы помним, что операнды арифметических операторов должны быть числового типа. Однако в Java допустим и тип char, потому что здесь это по сути разновидность типа int.
Проверим, как это работает с инкрементом и декрементом.
Пример 2
Работает: инкремент помещает в символьную переменную следующий знак из кодировки ASCII, а декремент — предыдущий.
Пример 3
На каждой итерации цикла значение переменной i выводится в консоль, а сама переменная увеличивается на один после каждого витка.
Примечание: если в примере выше заменить i++ на ++i, то результат в консоли не поменяется — проверьте.
Почему?
Дело в том, что преинкремент и постинкремент в условии цикла for можно сравнить с вызовом двух разных функций. Каждая из них делает одно и то же — увеличивает переменную i (после выполнения тела цикла), и только возвращаемые ими значения различаются.
Однако возвращаемое инкрементом значение в условии цикла использовать негде (нет выражения для вычисления) — поэтому оно просто выбрасывается.
Вот почему на работе цикла подобная замена не отразилась.
Пример 4
Перепишем пример с циклом так:
Как видим, вывод снова не изменился.
Всё потому, что в метод System.out.println () передаётся текущее значение переменной i, и лишь потом оно увеличивается на единицу.
За это отвечает постфиксная форма записи инкремента. А ещё есть префиксная. И для декремента тоже.
Префиксная и постфиксная формы
Синтаксис тут такой (x — переменная):
Операция | Постфиксная версия | Префиксная версия |
---|---|---|
Инкремент | x++ | ++x |
Декремент | х-- | --х |
Различия в работе:
- Префиксные операторы ++x (--x) записывают в переменную x изменённое значение и его же возвращают вместо себя (в исходное выражение).
- Постфиксные операторы x++ (x--) возвращают вместо себя текущее значение переменной х, а потом записывают в x новое значение.
Обратите внимание на слово потом. Потом — это когда? После вычисления всего выражения? Чтобы понять это, разберёмся с порядком вычисления инкрементов и декрементов.
Порядок вычисления выражений с операторами ++ и −−
Значения, которые инкременты возвращают в выражение, вычисляются до выполнения других операций. И то же самое для декрементов.
То есть у операторов ++ и -- наивысший приоритет при вычислении результата выражения.
О приоритете и ассоциативности
Не путайте приоритет с обычным порядком выполнения операторов. Все инструкции Java выполняет в привычном нам направлении (слева направо), и операнды операторов вычисляет так же.
Все арифметические операторы (за исключением унарных + и -; преинкремента ++x и предекремента --x) — левоассоциативны, то есть выполняются слева направо.
Приоритеты же определяют порядок выполнения операторов, которые сами являются частью более сложного (составного) арифметического или логического выражения.
Если на одном уровне встречаются операции одинакового приоритета, то какие из них выполнять первыми — определяет уже ассоциативность.
Пример 5
Разберём пример выше.
x = 3. Найдём значение выражения 2 * x++:
- Сперва вычисляем постинкремент x++, он возвращает в выражение текущее значение x, то есть 3, а потом увеличивает x на 1.
- Это эквивалентно исполнению кода: a = 2 * x; x = x + 1;
- Значение a стало равняться 6, а x равен 4.
y = 3. Вычислим значение выражения 2 * --y:
- Сперва вычисляем предекремент --y, он уменьшает y на 1 и возвращает вместо себя это новое значение.
- Это эквивалентно исполнению кода: y = y − 1; b = 2 * y;
Значение b стало равным 4, а y равен 2.
Выражение с несколькими инкрементами/декрементами
Если декременты или инкременты разделены каким-нибудь бинарным оператором (вроде +, -, *, /), то ассоциативностью они уже не связаны (так как приоритет бинарного оператора заведомо ниже). И тогда мы вычисляем значение выражения традиционным методом — слева направо, учитывая приоритет операций Java.
В этом случае стоит помнить, что:
- раньше всегда вычисляется декремент или инкремент, который стоит левее;
- скобки на порядок вычисления инкрементов и декрементов не влияют;
- конструкции типа ++x-- (--x++, ++x++ или --x--) запрещены.
Если в выражении много инкрементов/декрементов одной переменной
Тогда входным значением переменной для вычисления каждого последующего инкремента или декремента будет значение этой переменной после вычисления предыдущего инкремента или декремента.
То есть инкременты/декременты в выражении обрабатываются не одновременно, а по очереди, порядок в которой определяется ассоциативностью и приоритетом этих операторов в Java.
Рассмотрим это на примере.
Пример 6
Как думаете, почему значения такие?
Разберём пример выше.
x = 5. Начинаем вычислять выражение ++x + x--:
- Двигаемся слева направо до первого инкремента/декремента.
У нас это ++x (преинкремент x). Мы помним, что он увеличивает x на 1 и сразу возвращает вместо себя это новое значение x (5 + 1 = 6). - Подставляем 6 в наше выражение вместо ++x:
y = 6 + x--. - Вновь двигаемся слева направо до первого инкремента/декремента.
- Встречаем x-- (постдекремент x). Вспоминаем, что он возвращает вместо себя текущее значение x (которое у нас после инкремента равно 6).
- Подставляем 6 в вычисляемое выражение вместо x--. Получаем:
y = 6 + 6. - Вспоминаем, что не до конца исполнили постдекремент x, осталось уменьшить x на 1. Теперь значение x будет равно 6 − 1. То есть 5.
Вот мы и поработали процессорами (получили вручную те же самые значения):
х = 5;
y = 12.
А теперь пример посложнее (такие дают на экзаменах и собеседованиях).
Пример 7
Принципиально этот пример не отличается от предыдущего. Только будьте аккуратны при вычислениях. Потому что на этот раз инкременты и декременты меняют значение x трижды в одном выражении.
x = 4. Вычислим значение выражения ++x * 3 / x-- + --x.
Действуем как и раньше:
- Двигаемся слева направо до первого инкремента/декремента. Встречаем ++x (преинкремент x).
Как помним, он увеличивает x на 1 и сразу возвращает вместо себя новое значение x (4 + 1 = 5).
Подставляем 5 в наше выражение вместо ++x:
5 * 3 / x-- + --x. - Идём слева направо до первого инкремента/декремента. Встречаем x-- (постдекремент x), заменяем его текущим значением x. Переписываем наше выражение:
5 * 3 / 5 + --x.
Вспоминаем, что не до конца исполнили постдекремент x, осталось уменьшить x на 1. Теперь значение x станет равно 5 − 1. То есть 4. - Вновь двигаемся слева направо до первого инкремента/декремента:
5 * 3 / 5 + --x.
Встречаем --x (предекремент x), он уменьшает x на 1 и сразу возвращает вместо себя новое значение x (4 − 1 = 3).
Подставляем 3 в наше выражение вместо --x: y = 5 * 3 / 5 + 3. - Вспоминаем приоритет арифметических операций в Java — и вычисляем значение y.
Узнаём, что y = 6, а x = 3.
Запустим код и сверимся с результатом в консоли:
Совпало, ура!
Подытожим
Мы познакомились с инкрементом и декрементом, с их префиксной и постфиксной формами в Java, научились вычислять сложные выражения с ними.
Советуем сразу же потренироваться. Проверить себя можно в любом онлайн-компиляторе Java (даже со смартфона). Удачи на экзамене и собеседовании!