When implementing Comparable overriding equals and hashcode is must?

Whenever I implement Comparable Interface then should I have to override equals() and hashcode().

Let Suppose, I have to use Priority Queue and I have custom class. So to use Priority Queue I have to implement Comparable. But my confussion is It might have two different order and it basically depends on Priority Queue’s Implementation.

CASE 1 → If Priority Queue internally uses == to compare
CASE 2 → If Priority Queue internally uses compareTo to compare

CASE 1 and CASE 2 will return different order

class TestNode(val vertex : Int, var weight : Int = Int.MAX_VALUE) : Comparable<TestNode> {

    override fun hashCode(): Int {
        return vertex
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as TestNode

        if (vertex != other.vertex) return false

        return true
    }

    override fun compareTo(other: TestNode): Int = when {
        this.weight < other.weight -> -1
        this.weight > other.weight -> 1
        else -> 0
    }
}

fun isEqual(a : TestNode, b : TestNode) = (a == b)
fun isEqualByCompare(a : TestNode, b : TestNode) = a.compareTo(b) == 0

fun main() {
    val a = TestNode(1, 34)
    val b = TestNode(9, 34)
    println("${isEqual(a,b)}")
    println("${isEqualByCompare(a,b)}")
}

I checked Priority Queue’s implementation and got the information that its uses compareTo. But what about other peoples code. They might use == or compareTo.

Am I missing something very basic? Can someone give some insight?

1 Like

Why do you think PriorityQueue ever uses equals()? Its documentation clearly states it uses comparators. Also, I don’t see how equals() could be used for sorting. It only provides info about equality, but it doesn’t say which item is smaller/bigger.

1 Like

I don’t think Kotlin should enforce overriding equals() and hashCode(), since there are cases where failing to do so is entirely justifiable.

The docs for java.util.Comparable explicitly allow for orderings that are not ‘consistent with equals’.

They say that consistency with equals (where a.compareTo(b) == 0 has the same value as a.equals(b) for every a and b of that class) is ‘strongly recommended’, and that e.g. sorted sets and sorted maps are likely to have problems with keys which have a natural ordering inconsistent with equals.

But they give one case where lack of consistency with equals makes sense: BigDecimal. The BigDecimal values “1” and “1.0” are not equal, because they have different precisions; however, neither is larger or smaller than the other, and so compareTo() should return 0.

(Of course, the situation for kotlin.Comparable is identical, though its docs don’t mention any of this. It’s one of many cases where the Kotlin docs fall far short of the very high standard of the Java docs, and it’s well worth checking out the latter where appropriate.)

If a class has a natural ordering which is not consistent with equals, then that should be documented very clearly, to avoid nasty surprises. And similarly, classes which require an ordering consistent with equals should document that too. (Classes in the standard library such as SortedSet do indeed make that very clear.)

But no, I don’t think Kotlin should enforce overriding equals() and hashCode().

An IDE inspection could be a good idea, though, to ensure that failing to do is a deliberate decision and not just an oversight.

2 Likes