Оператор по модулю в Java

1. Обзор

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

2. Оператор по модулю

Начнем с недостатков простого деления в Java.

Если операнды с обеих сторон оператора деления имеют тип int , результатом операции будет другой тип int:

@Test public void whenIntegerDivision_thenLosesRemainder() { assertThat(11 / 4).isEqualTo(2); }

То же деление дает нам другой результат, если хотя бы один из операндов имеет тип float или double:

@Test public void whenDoubleDivision_thenKeepsRemainder() { assertThat(11 / 4.0).isEqualTo(2.75); }

Мы можем заметить, что мы теряем остаток операции деления при делении целых чисел.

Оператор по модулю дает нам именно этот остаток:

@Test public void whenModulo_thenReturnsRemainder() { assertThat(11 % 4).isEqualTo(3); }

Остаток - это то, что остается после деления 11 (делимого) на 4 (делитель) - в данном случае 3.

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

И операция деления, и операция по модулю вызывают исключение ArithmeticException, когда мы пытаемся использовать ноль в качестве правого операнда:

@Test(expected = ArithmeticException.class) public void whenDivisionByZero_thenArithmeticException() { double result = 1 / 0; } @Test(expected = ArithmeticException.class) public void whenModuloByZero_thenArithmeticException() { double result = 1 % 0; }

3. Распространенные варианты использования

Чаще всего оператор по модулю используется для определения четности или нечетности данного числа.

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

@Test public void whenDivisorIsOddAndModulusIs2_thenResultIs1() { assertThat(3 % 2).isEqualTo(1); }

С другой стороны, если результат равен нулю (т.е. нет остатка), это четное число:

@Test public void whenDivisorIsEvenAndModulusIs2_thenResultIs0() { assertThat(4 % 2).isEqualTo(0); }

Еще одно хорошее применение операции по модулю - отслеживать индекс следующего свободного места в круговом массиве.

В простой реализации круговой очереди для значений типа int элементы хранятся в массиве фиксированного размера.

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

@Test public void whenItemsIsAddedToCircularQueue_thenNoArrayIndexOutOfBounds() { int QUEUE_CAPACITY= 10; int[] circularQueue = new int[QUEUE_CAPACITY]; int itemsInserted = 0; for (int value = 0; value < 1000; value++) { int writeIndex = ++itemsInserted % QUEUE_CAPACITY; circularQueue[writeIndex] = value; } }

Используя оператор по модулю, мы предотвращаем выпадение writeIndex за пределы массива, поэтому мы никогда не получим ArrayIndexOutOfBoundsException .

Однако, как только мы вставим более QUEUE_CAPACITY элементов, следующий элемент перезапишет первый.

4. Вывод

Оператор по модулю используется для вычисления остатка от целочисленного деления, которое в противном случае потеряно.

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

Код примера доступен в репозитории GitHub.