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.