Автор оригинала: Tom Hombergs.
1. Обзор
В этом коротком уроке мы покажем, что такое оператор по модулю и как мы можем использовать его с Java для некоторых распространенных случаев использования.
2. Оператор по модулю
Давайте начнем с недостатков простого разделения в Java.
Если операнды с обеих сторон оператора деления имеют тип инт , результатом операции является другое инт:
@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; } }
Используя оператор по модулю, мы предотвращаем выпадение индекса записи за пределы массива, поэтому мы никогда не получим исключение ArrayIndexOutOfBoundsException .
Однако, как только мы вставим больше, чем QUEUE_CAPACITY элементов, следующий элемент перезапишет первый.
4. Заключение
Оператор по модулю используется для вычисления остатка от целочисленного деления, которое в противном случае было потеряно.
Полезно делать простые вещи, такие как выяснение, является ли данное число четным или нечетным, а также более сложные задачи, такие как отслеживание следующей позиции записи в круговом массиве.
Пример кода доступен в репозитории GitHub .