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

Оператор по модулю в 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 repository.