HashKode icon indicating copy to clipboard operation
HashKode copied to clipboard

Kotlin hashcode utilities

Notice: This library in no longer maintained and may no longer compile. See moroccode for similar functionality.

HashKode

HashKode is a small Kotlin hashcode utility to help you create easy and bugfree hashcodes and equality checks.

Why?

Java identifies objects by their hashcode and their equals method. If you do not override these methods, the JVM can only check objects for referential equality, not structural equality. (Read more)

Kotlin generates these methods automatically when you create a data class, but sometimes this is not preferred.

Implementing hashcode and equals can be tedious, verbose and is bug prone. HashKode provides concise ways to override these methods.

Features

  • Lightweight: 14.2 KB
  • Very fast
  • Designed with Josh Bloch's Effective Java in mind
  • Uses Java hashCode methods: tested, high quality hashes
  • Able to list differences

Download

HashKode can be downloaded from the Maven Central Repository.

HashKode is released under the MIT license.

Alternatives


How to use

  • Hashcodes
  • Equality
  • Difference

Hashcodes

HashKode makes creating hashes of your fields extremely convenient: just pass your fields to hashKode.

override fun hashCode() = hashKode(
    field1, field2, field3, field4
)

hashKode needs an odd number and a prime number to calculate hashes. (By default: 17 and 37.) When changing these values, HashKode validates them. To disable this behaviour, you can set HashKode.VERIFY_HASHKODE_PARAMETERS to false.

HashKode.VERIFY_HASHKODE_PARAMETERS = false 
// ... 
override fun hashCode() = hashKode(field1, field2, initialOddNumber = 3, multiplierPrime = 7) 

Equality

Checking two objects for structural equality can be done by overriding the equals method and using HashKodes compareFields.

override fun equals(other: Any?) = compareFields(other)
{
    // Compare in here
}

In compareFields, 3 fields are available:

  1. one : The first object (this)
  2. two : The second object (other)
  3. equal : Indicates if the objects are equal

Abstraction

There are 3 levels of abstraction available*.

  1. compareField
  2. correspondsTo
  3. Regular (x == y)

You can add custom comparators by using compareBy.

CompareField

compareField takes a method reference to a getter and automatically uses this getter on the two objects being compared.

override fun equals(other: Any?) = compareFields(other)
{
    compareField(Example::field1)
    compareField(Example::field2)
    compareField(Example::field3)
}

CorrespondsTo

correspondsTo compares two fields you pass to it. It declares that a field should correspond to another field.

override fun equals(other: Any?) = compareFields(other)
{
    one.field1 correspondsTo two.field1
    one.field2 correspondsTo two.field2
    one.field3 correspondsTo two.field3
}

Note that it is possible to let a field correspond to a different field: one.field1 correspondsTo two.field2.

Regular (x == y)

The fastest way to check fields for equality is a regular == check. Remember to set the equal field manually.

override fun equals(other: Any?) = compareFields(other)
{
    equal = one.field1 == two.field1 &&
            one.field2 == two.field2 &&
            one.field3 == two.field3
}

*Note that higher abstraction means lower performance, but this probably will not be an issue. See benchmarks.


Difference

HashKode can list differences between fields of objects. The API is the same as when using compareFields.

val tester1 = Example(field = "Hello")
val tester2 = Example(field = "World")

val diff = tester1.getDifferences(tester2)
{
    compareField(BasicTester::field)
}

diff will now contain a single FieldDifference object, since one different field was found. FieldDifference contains 2 pairs of a field and its owner object.

A custom comparison can be done by using differenceBy.


Benchmarks

HashKode can outperform alternative libraries.

Benchmark setup

  • Intel core i5 6600k @3.50Ghz
  • 16 GB DDR4 2400MHz RAM
  • Windows 10.0.14393

Benchmark details

  • Object with 5 fields:
    • Integer (index)
    • String (Hello World)
    • Double (Math.PI)
    • Boolean (true)
    • Array<String> (["A", "B", "C", "D", "E"])
  • Asserted equals check repeated 50.000.000
  • Test ran 3 times
  • Libraries tested: HashKode, Guava, Apache

CompareField

Library Average time per equals check
HashKode 48.3 ns
Guava 33.1 ns
Apache 29.7 ns

CorrespondsTo

Library Average time per equals check
HashKode 39.6 ns
Guava 33.2 ns
Apache 29.8 ns

Regular (x == y)

Library Average time per equals check
HashKode 28.4 ns
Guava 33.1 ns
Apache 29.7 ns