From 232d9957dc7de3b03990d31fcadd65a5a4f1facd Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Thu, 22 Aug 2024 22:22:56 +0200 Subject: [PATCH] Improve hashCode() implementation for Tuple* --- generator/Generator.sc | 2 +- src-gen/main/java/io/vavr/Tuple2.java | 2 +- src/test/java/io/vavr/TupleTest.java | 2 +- .../java/io/vavr/collection/AbstractMapTest.java | 7 +++++++ .../io/vavr/collection/AbstractMultimapTest.java | 9 +++++++++ .../java/io/vavr/collection/HashMultimapTest.java | 14 ++++++++++++++ .../io/vavr/collection/LinkedHashMultimapTest.java | 14 ++++++++++++++ .../java/io/vavr/collection/TreeMultimapTest.java | 14 ++++++++++++++ 8 files changed, 61 insertions(+), 3 deletions(-) diff --git a/generator/Generator.sc b/generator/Generator.sc index c583bb8802..33bdb8f7ff 100644 --- a/generator/Generator.sc +++ b/generator/Generator.sc @@ -2236,7 +2236,7 @@ def generateMainClasses(): Unit = { @Override public int hashCode() { - return ${if (i == 0) "1" else if (i == 1) "Objects.hashCode(_1)" else s"""Objects.hash(${(1 to i).gen(j => s"_$j")(", ")})"""}; + return ${if (i == 0) "1" else if (i == 1) "Objects.hashCode(_1)" else if (i == 2) "Objects.hashCode(_1) ^ Objects.hashCode(_2)" else s"""Objects.hash(${(1 to i).gen(j => s"_$j")(", ")})"""}; } @Override diff --git a/src-gen/main/java/io/vavr/Tuple2.java b/src-gen/main/java/io/vavr/Tuple2.java index 8258f6a527..314673b788 100644 --- a/src-gen/main/java/io/vavr/Tuple2.java +++ b/src-gen/main/java/io/vavr/Tuple2.java @@ -394,7 +394,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(_1, _2); + return Objects.hashCode(_1) ^ Objects.hashCode(_2); } @Override diff --git a/src/test/java/io/vavr/TupleTest.java b/src/test/java/io/vavr/TupleTest.java index 82fde37bbb..9ae3589fac 100644 --- a/src/test/java/io/vavr/TupleTest.java +++ b/src/test/java/io/vavr/TupleTest.java @@ -164,7 +164,7 @@ public void shouldCreateTuple2FromEntry() { @Test public void shouldHashTuple2() { final Tuple2 t = tuple2(); - assertThat(t.hashCode()).isEqualTo(Objects.hash(t._1, t._2)); + assertThat(t.hashCode()).isEqualTo(new AbstractMap.SimpleEntry<>(t._1, t._2).hashCode()); } @Test diff --git a/src/test/java/io/vavr/collection/AbstractMapTest.java b/src/test/java/io/vavr/collection/AbstractMapTest.java index ace743ca71..6b050a2ea6 100644 --- a/src/test/java/io/vavr/collection/AbstractMapTest.java +++ b/src/test/java/io/vavr/collection/AbstractMapTest.java @@ -1441,4 +1441,11 @@ public void shouldNonNilGroupByIdentity() { assertThat(actual).isEqualTo(expected); } + // -- hashCode + + @Override + @Test + public void shouldCalculateDifferentHashCodesForDifferentTraversables() { + assertThat(mapOf('a', 2, 'b', 1).hashCode()).isNotEqualTo(mapOf('a', 1, 'b', 2).hashCode()); + } } diff --git a/src/test/java/io/vavr/collection/AbstractMultimapTest.java b/src/test/java/io/vavr/collection/AbstractMultimapTest.java index 041f4b53f7..a2eb9cf393 100644 --- a/src/test/java/io/vavr/collection/AbstractMultimapTest.java +++ b/src/test/java/io/vavr/collection/AbstractMultimapTest.java @@ -183,6 +183,8 @@ protected , T2> Multimap emptyMap() { abstract protected , V> Multimap mapOfPairs(K k1, V v1, K k, V v2, K k3, V v3); + abstract protected , V> Multimap mapOf(K k1, V v1, K k2, V v2); + abstract protected , V> Multimap mapOf(K key, V value); abstract protected , V> Multimap mapOf(java.util.Map map); @@ -1198,4 +1200,11 @@ public void shouldCollectUsingMultimap() { // java.lang.ClassCastException: io.vavr.collection.List$Cons cannot be cast to java.lang.Comparable } + // -- hashCode + + @Override + @Test + public void shouldCalculateDifferentHashCodesForDifferentTraversables() { + assertThat(mapOf("a", 2, "b", 1).hashCode()).isNotEqualTo(mapOf("a", 1, "b", 2).hashCode()); + } } diff --git a/src/test/java/io/vavr/collection/HashMultimapTest.java b/src/test/java/io/vavr/collection/HashMultimapTest.java index 31cf940350..1a5e44a6d9 100644 --- a/src/test/java/io/vavr/collection/HashMultimapTest.java +++ b/src/test/java/io/vavr/collection/HashMultimapTest.java @@ -98,6 +98,20 @@ protected , V> HashMultimap mapOfPairs(K k1, V v1, } } + @Override + protected , V> Multimap mapOf(K k1, V v1, K k2, V v2) { + switch (containerType) { + case SEQ: + return HashMultimap.withSeq().of(k1, v1, k2, v2); + case SET: + return HashMultimap.withSet().of(k1, v1, k2, v2); + case SORTED_SET: + return HashMultimap.withSortedSet(Comparators.naturalComparator()).of(k1, v1, k2, v2); + default: + throw new RuntimeException(); + } + } + @Override protected , V> HashMultimap mapOf(K key, V value) { switch (containerType) { diff --git a/src/test/java/io/vavr/collection/LinkedHashMultimapTest.java b/src/test/java/io/vavr/collection/LinkedHashMultimapTest.java index d3aca69932..5212e5b724 100644 --- a/src/test/java/io/vavr/collection/LinkedHashMultimapTest.java +++ b/src/test/java/io/vavr/collection/LinkedHashMultimapTest.java @@ -98,6 +98,20 @@ protected , V> LinkedHashMultimap mapOfPairs(K k1, } } + @Override + protected , V> Multimap mapOf(K k1, V v1, K k2, V v2) { + switch (containerType) { + case SEQ: + return LinkedHashMultimap.withSeq().of(k1, v1, k2, v2); + case SET: + return LinkedHashMultimap.withSet().of(k1, v1, k2, v2); + case SORTED_SET: + return LinkedHashMultimap.withSortedSet(Comparators.naturalComparator()).of(k1, v1, k2, v2); + default: + throw new RuntimeException(); + } + } + @Override protected , V> LinkedHashMultimap mapOf(K key, V value) { switch (containerType) { diff --git a/src/test/java/io/vavr/collection/TreeMultimapTest.java b/src/test/java/io/vavr/collection/TreeMultimapTest.java index e5b53c9d69..3a3051666c 100644 --- a/src/test/java/io/vavr/collection/TreeMultimapTest.java +++ b/src/test/java/io/vavr/collection/TreeMultimapTest.java @@ -98,6 +98,20 @@ protected , V> TreeMultimap mapOfPairs(K k1, V v1, } } + @Override + protected , V> Multimap mapOf(K k1, V v1, K k2, V v2) { + switch (containerType) { + case SEQ: + return TreeMultimap.withSeq().of(k1, v1, k2, v2); + case SET: + return TreeMultimap.withSet().of(k1, v1, k2, v2); + case SORTED_SET: + return TreeMultimap.withSortedSet(Comparators.naturalComparator()).of(k1, v1, k2, v2); + default: + throw new RuntimeException(); + } + } + @Override protected , V> TreeMultimap mapOf(K key, V value) { switch (containerType) {