How to implement `equals` and `hashCode` on `List` subtypes?


#1

I just noticed that, in contrast to what is done in Java, the List interface in Kotlin does not define either equals or hashCode methods.

This is also true for other interfaces like Set and Map.

From what I understand, this means that the following statements are not necessarily true (although they actually are in practice):

  • listOf(1) == arrayListOf(1)
  • listOf(1).hashCode == arrayListOf(1).hashCode

So, this leads to the following questions:

  • Why doesn’t the List interface in Kotlin explicitly override equals and hashCode (as its counterpart in Java does)?
  • Say I want to create a custom List implementation called MyEmptyList. Instances from this class are always empty. This means that MyEmptyList() == emptyList() should be always true. How should hashCode be implemented in this case?

#2

As far as I know Javas List interface does not override hashCode or equals. The interface declares that each list needs to implement hashCode and equals. The jdoc documentation just states how this should be implemented.
This should also answer your second question.
Kotlins Lists do not have equals and hashCode as part of the interface description because all classes in Kotlin are a subclass of Any which declares hashCode and equals already.

If you look at the code of ArrayList for example you see that it extends AbstractList which implements hashCode. There is also an equivalent class called AbstractSequentialList which should be used for lists backed by sequentially accessed data.


#3

I also stumbled over this and I have to disagree with Wasabi375.

It’s true, Javas List interface does not actually implement equals (which would be impossible), but it overwrites the JavaDoc to clearly state, what a List.equals method must fullfil. It must compare elements by index etc. This is much more specific then just relying on the general contract of Object.equals (or in Kotlin Any.equals).

So following this, in Java, I can be sure that any lists are equal if they have equal elements. Since Kotlin wants to be compatible (and in practice it is) the KDocs of List.equals and Set.equals etc. should also be aligned to Java.


#4

This is one thing I don’t like about Kotlin. Compared to Java, the documentation and method contracts are rather unspecified.

Java says how equals and hashCode for various collections should be implemented. Java specifies that CharSequence.toString() should return a String containing the same characters the CharSequence represents. Java specifies which exceptions should occur if List.get() is called with an out-of-bounds index. And so on. Kotlin specifies nothing like that. Assuming you don’t know about the Java conventions or you ignore them, Code you write in Kotlin will likely violate Java specifications. That’s not a good situation and will cause problems sooner or later.