0

I wanted to use a LinkedHashSet as a key in the Map, but since the hashCode() of the LinkedHashSet object doesn't take the order of elements into consideration, my sets are considered as the same key in the map.

        Map<LinkedHashSet<String>, Integer> ballotsAsSets = new HashMap<>();
        for (Map.Entry<List<String>, Integer> e : ballots.entrySet()) {
            LinkedHashSet<String> newKey = new LinkedHashSet<>(e.getKey());
            System.out.println("key = " + newKey + ", hash = " + newKey.hashCode());
            ballotsAsSets.put(newKey, e.getValue());
        }
ballots = {[A, B, C]=1, [B, A, C]=3, [A, C, B]=1}
key = [A, B, C], hash = 198
key = [B, A, C], hash = 198
key = [A, C, B], hash = 198

It seems counterintuitive.

Ihor M.
  • 2,728
  • 3
  • 44
  • 70
  • So to clarify, you want to consider different order as a different key? If so, why did you choose a set? – WJS Mar 29 '23 at 15:56
  • Fundamentally a `LinkedHashSet` is a `Set` and follow `Set` semantics. Therefore `new LinkedHashSet<>(List.of("A", "B")).equals(new LinkedHashSet<>(List.of("B", "A"))` is `true`. – Joachim Sauer Mar 29 '23 at 15:56
  • Because LinkedHashSet has the following unique properties: it doesn't let duplicates in and has predictive iterations. And I wanted such an object to be also unique.. – Ihor M. Mar 29 '23 at 18:31
  • 2
    The sets are not considered equal because they have the same hash code, they have the same hash code because they are equal. Two objects are equal when their `equals` method says so, regardless of their hash code. – Holger Mar 30 '23 at 07:59
  • Ok, that last explanation makes sense to me: LinkedHashSet is still a Set and needs to behave like one, disregarding the predictable order of iteration. – Ihor M. Mar 30 '23 at 16:56

1 Answers1

2

Set::equals:

Compares the specified object with this set for equality. Returns true if the specified object is also a set, the two sets have the same size, and every member of the specified set is contained in this set (or equivalently, every member of this set is contained in the specified set). This definition ensures that the equals method works properly across different implementations of the set interface.

and hashCode needs to be consistent with equals.

It's dangerous to use mutable keys in Maps anyway, so you may as well convert it to an immutable list.

Michael
  • 41,989
  • 11
  • 82
  • 128
  • I mean [`Set::hashCode`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Set.html#hashCode()) basically tells you the exact formula for the hashcode that implementations **have to** use. – Joachim Sauer Mar 29 '23 at 15:50
  • Who is stopping you to extend from LinkedHashSet and then override equals and hashCode? – Ihor M. Mar 29 '23 at 18:29
  • 1
    @IhorM. No one, but such a class violates the contract of the interfaces it implements, which is likely to lead to bugs. Who is stopping you from using a List? – Michael Mar 29 '23 at 20:00
  • I further used LinkedHasSet to check if an item is in the collection and to preserve order of insertion. – Ihor M. Mar 29 '23 at 22:58
  • So, I have to copy data to an immutable list. – Ihor M. Mar 29 '23 at 22:59
  • Anyways, it’s ok. I was surprised to see that hashCode() is not computed in a similar way as in a list. – Ihor M. Mar 29 '23 at 23:00
  • 2
    @IhorM. you can implement your own collection that is neither `Set` nor `List` but has the desired properties. Internally, it may delegate to a `LinkedHashSet` (except for `equals` and `hashCode`)… – Holger Mar 31 '23 at 13:52
  • @Holger yes, I have arrived at the same conclusion, thanks for your opinion – Ihor M. Mar 31 '23 at 16:12