Skip to content

For container types, check hash code before deep equality #58

@dlurton

Description

@dlurton

For example, the StructElementImpl class implements Any.equals using the code shown below. Checking if two containers are equal could be considerably faster if the Any.equals override for each container type considered the Any.hashCode before checking deep equality. The hash code is already lazily computed and never re-computed, so this has the potential to significantly increase performance for certain uses of this library.

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

        // TODO: compare hash code here--if not equal, values are not equal

        if (annotations != other.annotations) return false

        // We might avoid materializing fieldsByName by checking fields.size first
        if (this.size != other.size) return false
        if (this.fieldsByName.size != other.fieldsByName.size) return false

        // If we make it this far we can compare the list of field names in both
        if(this.fieldsByName.keys != other.fieldsByName.keys) return false

        // If we make it this far then we have to take the expensive approach of comparing the individual values in
        // [this] and [other].
        //
        // A field group is a list of fields with the same name. Within each field group we count the number of times
        // each value appears in both [this] and [other]. Each field group is equivalent if every value that appears n
        // times in one group also appears n times in the other group.

        this.fieldsByName.forEach { thisFieldGroup ->
            val thisSubGroup: Map<AnyElement, Int> = thisFieldGroup.value.groupingBy { it }.eachCount()

            // [otherGroup] should never be null due to the `if` statement above.
            val otherGroup = other.fieldsByName[thisFieldGroup.key]
                             ?: error("unexpectedly missing other field named '${thisFieldGroup.key}'")

            val otherSubGroup: Map<AnyElement, Int> = otherGroup.groupingBy { it }.eachCount()

            // Simple equality should work from here
            if(thisSubGroup != otherSubGroup) {
                return false
            }
        }

        // Metas intentionally not included here.

        return true
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions