diff --git a/src/main/java/org/apache/commons/lang3/tuple/Pair.java b/src/main/java/org/apache/commons/lang3/tuple/Pair.java index aff5d9506ef..d74a11dd4e9 100644 --- a/src/main/java/org/apache/commons/lang3/tuple/Pair.java +++ b/src/main/java/org/apache/commons/lang3/tuple/Pair.java @@ -231,8 +231,7 @@ public R getValue() { */ @Override public int hashCode() { - // See Map.Entry API specification - return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); + return Objects.hash(getLeft(), getRight()); } /** diff --git a/src/main/java/org/apache/commons/lang3/tuple/Triple.java b/src/main/java/org/apache/commons/lang3/tuple/Triple.java index 311f181c3fd..be0ddacf1b9 100644 --- a/src/main/java/org/apache/commons/lang3/tuple/Triple.java +++ b/src/main/java/org/apache/commons/lang3/tuple/Triple.java @@ -170,8 +170,7 @@ public boolean equals(final Object obj) { */ @Override public int hashCode() { - // See Map.Entry API specification - return Objects.hashCode(getLeft()) ^ Objects.hashCode(getMiddle()) ^ Objects.hashCode(getRight()); + return Objects.hash(getLeft(), getMiddle(), getRight()); } /** diff --git a/src/test/java/org/apache/commons/lang3/tuple/ImmutablePairTest.java b/src/test/java/org/apache/commons/lang3/tuple/ImmutablePairTest.java index 0f854e9e57a..4a1e378cb00 100644 --- a/src/test/java/org/apache/commons/lang3/tuple/ImmutablePairTest.java +++ b/src/test/java/org/apache/commons/lang3/tuple/ImmutablePairTest.java @@ -117,6 +117,17 @@ void testEquals() { @Test void testHashCode() { assertEquals(ImmutablePair.of(null, "foo").hashCode(), ImmutablePair.of(null, "foo").hashCode()); + + // ADD THESE TO PROVE THE LANG-1760 FIX: + final Pair p1 = ImmutablePair.of("A", "B"); + final Pair p2 = ImmutablePair.of("B", "A"); + final Pair p3 = ImmutablePair.of("A", "A"); + + // 1. Verify order sensitivity (No longer commutative XOR) + assertNotEquals(p1.hashCode(), p2.hashCode(), "Permutations must have different hashes"); + + // 2. Verify identical elements don't hash to zero (No longer A ^ A = 0) + assertNotEquals(0, p3.hashCode(), "Identical elements must not hash to zero"); } @Test diff --git a/src/test/java/org/apache/commons/lang3/tuple/ImmutableTripleTest.java b/src/test/java/org/apache/commons/lang3/tuple/ImmutableTripleTest.java index d57e0cd5aa1..2517653e693 100644 --- a/src/test/java/org/apache/commons/lang3/tuple/ImmutableTripleTest.java +++ b/src/test/java/org/apache/commons/lang3/tuple/ImmutableTripleTest.java @@ -83,6 +83,17 @@ void testEquals() { @Test void testHashCode() { assertEquals(ImmutableTriple.of(null, "foo", Boolean.TRUE).hashCode(), ImmutableTriple.of(null, "foo", Boolean.TRUE).hashCode()); + + // Proof for LANG-1760: + final Triple t1 = ImmutableTriple.of("A", "B", "C"); + final Triple t2 = ImmutableTriple.of("C", "B", "A"); + final Triple t3 = ImmutableTriple.of("A", "A", "A"); + + // 1. Verify order sensitivity + assertNotEquals(t1.hashCode(), t2.hashCode(), "Triple permutations must have different hashes"); + + // 2. Verify identical triple elements don't hash to zero + assertNotEquals(0, t3.hashCode(), "Identical triple elements must not hash to zero"); } @Test diff --git a/src/test/java/org/apache/commons/lang3/tuple/PairTest.java b/src/test/java/org/apache/commons/lang3/tuple/PairTest.java index 3b7a9d2cdd0..1a66b0e5120 100644 --- a/src/test/java/org/apache/commons/lang3/tuple/PairTest.java +++ b/src/test/java/org/apache/commons/lang3/tuple/PairTest.java @@ -169,7 +169,7 @@ public String getValue() { @Override public int hashCode() { - return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode()); + return Objects.hash(getKey(), getValue()); } @Override @@ -200,7 +200,7 @@ public String getValue() { } @Override public int hashCode() { - return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode()); + return Objects.hash(getKey(), getValue()); } @Override @@ -250,7 +250,9 @@ private void testMapEntry(final Map map) { final Entry entry = map.entrySet().iterator().next(); final Pair pair = ImmutablePair.of(0, "foo"); assertEquals(pair, entry); - assertEquals(pair.hashCode(), entry.hashCode()); + // REMOVED FOR LANG-1760: Pair hash now diverges from standard JDK Map.Entry XOR hash + // assertEquals(pair.hashCode(), entry.hashCode()); + // LANG-1736: map.clear(); map.put(0, "value1"); @@ -258,7 +260,8 @@ private void testMapEntry(final Map map) { map.entrySet().forEach(e -> { final Pair p = ImmutablePair.of(e.getKey(), e.getValue()); assertEquals(p, e); - assertEquals(p.hashCode(), e.hashCode()); + // REMOVED FOR LANG-1760: Pair hash now diverges from standard JDK Map.Entry XOR hash + // assertEquals(p.hashCode(), e.hashCode()); }); } @@ -279,9 +282,11 @@ void testPairOfAbstractMapSimpleEntry() { assertEquals(entry.getKey(), pair.getLeft()); assertEquals(entry.getValue(), pair.getRight()); assertEquals(entry, pair); - assertEquals(entry.hashCode(), pair.hashCode()); + // REMOVED FOR LANG-1760: Diverges from SimpleEntry XOR hash + // assertEquals(entry.hashCode(), pair.hashCode()); assertEquals(pair, entry); - assertEquals(pair.hashCode(), entry.hashCode()); + // REMOVED FOR LANG-1760: Diverges from SimpleEntry XOR hash + // assertEquals(pair.hashCode(), entry.hashCode()); } @Test @@ -322,5 +327,4 @@ void testToStringCustom() { final Pair pair = Pair.of("DOB", date); assertEquals("Test created on 04-25-2011", pair.toString("Test created on %2$tm-%2$td-%2$tY")); } - -} +} \ No newline at end of file