1. Översikt
I den här korta handledningen ska vi visa vad modulo-operatören är och hur vi kan använda den med Java för vissa vanliga användningsfall.
2. Modulo-operatören
Låt oss börja med bristerna i enkel uppdelning i Java.
Om operanderna på båda sidor av divisionsoperatören har typ int är resultatet av operationen ett annat int:
@Test public void whenIntegerDivision_thenLosesRemainder() { assertThat(11 / 4).isEqualTo(2); }
Samma uppdelning ger oss ett annat resultat när minst en av operanderna har typ float eller double:
@Test public void whenDoubleDivision_thenKeepsRemainder() { assertThat(11 / 4.0).isEqualTo(2.75); }
Vi kan observera att vi förlorar resten av en deloperation när vi delar heltal.
Moduloperatören ger oss exakt den här återstoden:
@Test public void whenModulo_thenReturnsRemainder() { assertThat(11 % 4).isEqualTo(3); }
Resten är vad som återstår efter att ha delat 11 (utdelningen) med 4 (delaren) - i det här fallet 3.
Av samma anledning är det inte möjligt att dela med noll, det är inte möjligt att använda moduloperatorn när högerargumentet är noll.
Både delningen och moduloperationen ger ett ArithmeticException när vi försöker använda noll som högeroperand:
@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. Vanliga användningsfall
Det vanligaste användningsfallet för moduloperatören är att ta reda på om ett givet nummer är udda eller jämnt.
Om resultatet av moduloperationen mellan valfritt tal och två är lika med ett är det ett udda tal:
@Test public void whenDivisorIsOddAndModulusIs2_thenResultIs1() { assertThat(3 % 2).isEqualTo(1); }
Å andra sidan, om resultatet är noll (dvs. det finns ingen återstod), är det ett jämnt tal:
@Test public void whenDivisorIsEvenAndModulusIs2_thenResultIs0() { assertThat(4 % 2).isEqualTo(0); }
En annan bra användning av moduloperationen är att hålla reda på indexet för nästa lediga plats i en cirkulär grupp.
I en enkel implementering av en cirkulär kö för int- värden förvaras elementen i en matris med fast storlek.
Varje gång vi vill driva ett element till vår cirkulära kö, beräknar vi bara nästa lediga position genom att beräkna modulo för antalet objekt som vi redan har lagt in plus 1 och kökapaciteten:
@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; } }
Med hjälp av modulo-operatören förhindrar vi writeIndex att falla utanför gruppens gränser, därför får vi aldrig ett ArrayIndexOutOfBoundsException .
Men när vi har infogat mer än QUEUE_CAPACITY objekt kommer nästa objekt att skriva över det första.
4. Slutsats
Moduloperatören används för att beräkna resten av ett heltal som annars förlorat.
Det är användbart att göra enkla saker som att ta reda på om ett visst nummer är jämnt eller udda, liksom mer komplexa uppgifter som att spåra nästa skrivposition i en cirkulär grupp.
Exempelkoden är tillgänglig i GitHub-arkivet.