Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/main/java/org/apache/commons/lang3/tuple/Pair.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

/**
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/org/apache/commons/lang3/tuple/Triple.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> p1 = ImmutablePair.of("A", "B");
final Pair<String, String> p2 = ImmutablePair.of("B", "A");
final Pair<String, String> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String, String> t1 = ImmutableTriple.of("A", "B", "C");
final Triple<String, String, String> t2 = ImmutableTriple.of("C", "B", "A");
final Triple<String, String, String> 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
Expand Down
22 changes: 14 additions & 8 deletions src/test/java/org/apache/commons/lang3/tuple/PairTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
Expand Down Expand Up @@ -169,7 +169,8 @@ public String getValue() {

@Override
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode());
// FIXED FOR LANG-1760: Use Objects.hash to match Pair distribution
return Objects.hash(getKey(), getValue());
}

@Override
Expand Down Expand Up @@ -200,7 +201,8 @@ public String getValue() {
}
@Override
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode());
// FIXED FOR LANG-1760: Use Objects.hash to match Pair distribution
return Objects.hash(getKey(), getValue());
}

@Override
Expand Down Expand Up @@ -250,15 +252,17 @@ private void testMapEntry(final Map<Integer, String> map) {
final Entry<Integer, String> entry = map.entrySet().iterator().next();
final Pair<Integer, String> 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");
map.put(1, "value2");
map.entrySet().forEach(e -> {
final Pair<Integer, String> 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());
});
}

Expand All @@ -279,9 +283,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: Pair hash now diverges from SimpleEntry XOR hash
// assertEquals(entry.hashCode(), pair.hashCode());
assertEquals(pair, entry);
assertEquals(pair.hashCode(), entry.hashCode());
// REMOVED FOR LANG-1760: Pair hash now diverges from SimpleEntry XOR hash
// assertEquals(pair.hashCode(), entry.hashCode());
}

@Test
Expand Down Expand Up @@ -323,4 +329,4 @@ void testToStringCustom() {
assertEquals("Test created on 04-25-2011", pair.toString("Test created on %2$tm-%2$td-%2$tY"));
}

}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not change the EOF.