diff --git a/kubernetes/src/main/java/io/kubernetes/client/custom/Quantity.java b/kubernetes/src/main/java/io/kubernetes/client/custom/Quantity.java index 8ab19aa875..c2940354b5 100644 --- a/kubernetes/src/main/java/io/kubernetes/client/custom/Quantity.java +++ b/kubernetes/src/main/java/io/kubernetes/client/custom/Quantity.java @@ -90,7 +90,10 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(number); + // equals() compares numbers by value (scale-insensitive via BigDecimal.compareTo), so the + // hash must also be scale-insensitive to honor the equals/hashCode contract. Normalize the + // scale before hashing; otherwise e.g. "1" and "1000m" are equal but hash differently. + return Objects.hash(number == null ? null : number.stripTrailingZeros()); } public static class QuantityAdapter extends TypeAdapter { diff --git a/kubernetes/src/test/java/io/kubernetes/client/custom/QuantityTest.java b/kubernetes/src/test/java/io/kubernetes/client/custom/QuantityTest.java index f86ada4b2c..8ef4c39ebb 100644 --- a/kubernetes/src/test/java/io/kubernetes/client/custom/QuantityTest.java +++ b/kubernetes/src/test/java/io/kubernetes/client/custom/QuantityTest.java @@ -30,4 +30,17 @@ void containsInSet() { assertEquals(set1, set2, "Sets of equal elements are not equal"); } + + @Test + void equalQuantitiesWithDifferentScaleHaveEqualHashCode() { + // "1" parses to scale 0; "1000m" parses to 1000 * 10^-3 = 1.000 (scale 3). They are equal by + // value, so the equals/hashCode contract requires equal hash codes (and HashSet membership). + final Quantity a = Quantity.fromString("1"); + final Quantity b = Quantity.fromString("1000m"); + + assertEquals(a, b, "quantities with the same value should be equal"); + assertEquals( + a.hashCode(), b.hashCode(), "equal quantities must have equal hash codes (equals/hashCode contract)"); + assertEquals(Set.of(a), Set.of(b), "equal quantities must be interchangeable as hash-set members"); + } }