Apache Hash Code and Equals Builders

In the post I want to present two useful utility classes that for a long time are used in Java world and developed within Apache Software Foundation. These are HashCodeBuilder and EqualsBuilder classes which were ported by me in C#. To implement a good method of Hash Code and Equals for any class is not an easy task, but the classes assists implementing object.GetHashCode and object. Equals.

For equality comparation of objects should be used all relevant fields of the object, derived fields could be excluded. In order to build a Hash Code for an object is recommended to use same fields that were used for equality.

I like to learn by examples, so let’s to start one.

        public class RgbColor

        {

            private readonly ushort r;

            private readonly ushort g;

            private readonly ushort b;

 

            public RgbColor(ushort r, ushort g, ushort b)

            {

                this.r = r;

                this.g = g;

                this.b = b;

            }

 

            public ushort R

            {

                get { return r; }

            }

 

            public ushort B

            {

                get { return b; }

            }

 

            public ushort G

            {

                get { return g; }

            }

That is an simple immutable POCO class that represents an RGB Color, and a typical implementation for the Equals and Hash Code are similar to:

           public bool Equals(RgbColor other)

            {

                return other.r == r && other.g == g && other.b == b;

            }

 

            public override int GetHashCode()

            {

                int result = r.GetHashCode();

                result = (result * 397) ^ g.GetHashCode();

                result = (result * 397) ^ b.GetHashCode();

                return result;

            }

[NOTE: irrelevant code blocks are omitted]

TIP: these methods were generated using ReSharper.

As we can see same logic is repeated especially same mathematical and logical operations every time, DRY principle will argue us and finally the embracement will not let us sleep. What to do? Get some drugs?

Of course NO! Now we a lucky, we will apply HashCodeBuilder and and EqualsBuilder classes.

            public bool Equals(RgbColor other)

            {

                return new EqualsBuilder()

                    .Append(R, other.R)

                    .Append(G, other.G)

                    .Append(B, other.B)

                    .IsEquals();                   

            }

 

            public override int GetHashCode()

            {

                return new HashCodeBuilder()

                    .Append(R)

                    .Append(G)

                    .Append(B)

                    .ToHashCode();

            }

With three primitive properties all is simple and nice but how to deal when we have collections, arrays, jagged arrays ect..?

Flowing test demonstrate Append method generality:

        [Test]

        public void testRaggedArray()

        {

            var array1 = new long[2][];

            var array2 = new long[2][];

 

 

            for (int i = 0; i < array1.Length; ++i)

            {

                array1[i] = new long[2];

                array2[i] = new long[2];

                for (int j = 0; j < array1[i].Length; ++j)

                {

                    array1[i][j] = (i + 1) * (j + 1);

                    array2[i][j] = (i + 1) * (j + 1);

                }

            }

            Assert.IsTrue(new EqualsBuilder().Append(array1, array1).IsEquals());

            Assert.IsTrue(new EqualsBuilder().Append(array1, array2).IsEquals());

            array1[1][1] = 0;

            Assert.IsTrue(!new EqualsBuilder().Append(array1, array2).IsEquals());

        }

You can discover more usages by taking a look to the unit tests that are ported too. OR taking a look to java classes documentation because the usage is almost compatible.

http://commons.apache.org/lang/api/org/apache/commons/lang/builder/EqualsBuilder.html

 http://commons.apache.org/lang/api/org/apache/commons/lang/builder/HashCodeBuilder.html

http://www.java2s.com/Tutorial/Java/0500__Apache-Common/EqualsBuilder.htm

http://www.java2s.com/Tutorial/Java/0500__Apache-Common/HashCodeBuilder.htm

 I’m sure that could be other implementations, based on attributes for the class proprieties and common implementation for the hash code and equal in the base class using reflection. I’m talking about something similar to the Sharp Architecture approach. However even in the case could be used the Builder classes for various type handling flexibility.

The classes are ported as part of Domain Driven Design Sample implemented in C#, so you can find within the trunk real usages of the classes.

So source for the classes is here:

EqualsBuilder.cs

HashCodeBuilder.cs

About Domain Driven Design (DDD) Sample application I will blog more lately, for introduction in DDD in general please refer here.

For folks who want to know more about Builder pattern and Fluent interfaces can refer to my previous post.

Thank you for your attention,

Artur Trosin

6 Comments

  • So everytime hash function is called you are creating new object instance? hm...
    Most java IDEs can generate hash code functions.

  • Thank you for this! I've been using the Apache versions in my Java projects for a long time. I was going to port them to C# myself but you have already done the work for me. Thanks!

  • Thank you for this! I've been using the Apache versions in my Java projects for a long time. I was going to port them to C# myself but you have already done the work for me. Thanks!

  • Artur,

    I just compiled the builders and the tests with Visual C# 2008. Then I ran the tests with NUnit-2.5.0.9122. Two tests failed:

    NDDDSample.Tests.Infrastructure.Builders.EqualsBuilderTest.testReflectionEquals:
    Expected: True
    But was: False
    at NDDDSample.Tests.Infrastructure.Builders.EqualsBuilderTest.testReflectionEquals() in EqualsBuilderTest.cs:line 41


    NDDDSample.Tests.Infrastructure.Builders.EqualsBuilderTest.testReflectionHierarchyEquals:
    Expected: True
    But was: False

    at NDDDSample.Tests.Infrastructure.Builders.EqualsBuilderTest.testReflectionEqualsEquivalenceRelationship(TestObject to, TestObject toBis, TestObject toTer, TestObject to2, TestObject oToChange, Boolean testTransients) in EqualsBuilderTest.cs:line 190
    at NDDDSample.Tests.Infrastructure.Builders.EqualsBuilderTest.testReflectionHierarchyEquals(Boolean testTransients) in EqualsBuilderTest.cs:line 85
    at NDDDSample.Tests.Infrastructure.Builders.EqualsBuilderTest.testReflectionHierarchyEquals() in EqualsBuilderTest.cs:line 55

    Do they fail for you too?

  • Hi All,
    @bubak
    >> So everytime hash function is called you are creating new object instance? hm...
    Do you see some issues in creating new instance that could be GC collected immediately after method call?
    >> Most java IDEs can generate hash code functions.
    Sadly but for C# it’s not the case.

    @Joe,
    I’m glad you liked it.
    Regarding the tests, yes they do fail for me to… I didn’t spend too much time on fixing the 2 tests, because the primary goal for the project wasn’t to port apache lib but the project itself, so we didn’t need reflection equality methods we use other methods.
    I think, the problem is within the:
    private static void ReflectionAppend(
    Object lhs,
    Object rhs,
    Type clazz,
    EqualsBuilder builder,
    bool useTransients)
    Something is not ported properly. So if you have a fix please share it :).
    Artur

  • Thanks a lot for talking about useful review.

Comments have been disabled for this content.