28

Short question

How to implement GetHashCode for an Array.

Details

I have an object that overrides Equals, checking that:

this.array[n] == otherObject.array[n]

for all n in array.

Naturally I should implement the complementary GetHashCode. I was wondering if there is .NET way to do this, or if I should implement my own, something like

hash = hash ^ array[n]

Clarification

My object contains an array, and I'm interested on GetHashCode for the elements of the array. My code for array equivalence is for example only - like my question says but maybe I wasn't clear, I'm interested in GetHashCode (not Equals). I say I naturally should implement the complementary GetHashCode because it is a requirement of .NET to implement this once Equals is overridden (for Dictionary etc. to function correctly). Thanks.

5
  • Have a look at the answer posted here. In other words, you are better off implementing your own variation or using another tool, you can't use GetHashCode() or Equals() for an Array Commented May 9, 2016 at 14:21
  • Why not do this.array[n].Equals(otherObject.array[n]) for n? Commented May 9, 2016 at 14:21
  • 1
    If you want to compare two arrays for equality, you can use SequenceEqual extension Commented May 9, 2016 at 14:22
  • @c z: Please clarify whether array is a field in the object for which you're overriding Equals and GetHashCode. Commented May 9, 2016 at 15:14
  • Possible duplicate of GetHashCode override of object containing generic array Commented Jul 16, 2018 at 3:29

4 Answers 4

29

To compute a hash code using the elements of an array, you can cast the array to IStructuralEquatable and then call the GetHashCode(IEqualityComparer) method, passing a comparer for the type of elements in the array.

(The cast is necessary because the Array class implements the method explicitly.)

For example, if your object has an int array, then you can implement GetHashCode like this:

public override int GetHashCode()
{
    return ((IStructuralEquatable)this.array).GetHashCode(EqualityComparer<int>.Default);
}

In case you're curious, here's how the Array class implements the GetHashCode method (from the Reference Source):

internal static int CombineHashCodes(int h1, int h2) {
    return (((h1 << 5) + h1) ^ h2);
}

int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) {
    if (comparer == null)
        throw new ArgumentNullException("comparer");
    Contract.EndContractBlock();

    int ret = 0;

    for (int i = (this.Length >= 8 ? this.Length - 8 : 0); i < this.Length; i++) {
        ret = CombineHashCodes(ret, comparer.GetHashCode(GetValue(i)));
    }

    return ret;
}

As you can see, the current implementation only uses the last eight elements of the array.

Sign up to request clarification or add additional context in comments.

Comments

5

It depends on what you want ...

One option as Michael answered above is to have a hashcode based on the array elements. This will be in line with your Equals value semantics. However because "As a guideline, the hash of an object must be the same over the object's entire lifetime" you will have to ensure your array does not change after getting its hashcode. To have a non immutable container with a demand that it never changes sounds error prone to me.

Your other (IMO better option) is to switch to an immutable container (ie ImmutableArray) then a value-based hashcode makes sense. You can either use IStructuralEquatable as above or more generally:

    public override GetHashCode() =>
        Value.Aggregate(0, (total, next) => HashCode.Combine(total, next));

which will work for other immutable collections as well.

2 Comments

Using Array.GetHashCode() is definitely wrong because it will return different values for two arrays with equal elements, whereas the OP needs it to return the same value. Obviously, you have to ensure that the contents of the array are not modified after obtaining its structural hash code, which is possible to do if the array is a private member of an object. (Given that an array has a fixed size, I assume that's what you meant by "add/remove elements".)
you're right! edited my answer. It seems there is no 'good' solution to storing non-immutable collections with value semantics as elements of other collections
3

Using the current framework one could consider using

int value=0;
for (var i = 0;i< this.array.Length; i++)
{
    value=HashCode.Combine(this.array[i],value);
}

Comments

1

I don't agree you should naturally implement GetHashCode on an array
You would have to update it with every change
Or calculate it on the fly
I would compare directly on the fly
SequenceEquals will use the default equality comparer so you should also implement

public bool Equals

0n the objects in the array

Enumerable.SequenceEqual
Has an example

public static void SequenceEqualEx1()
{
    Pet pet1 = new Pet { Name = "Turbo", Age = 2 };
    Pet pet2 = new Pet { Name = "Peanut", Age = 8 };

    // Create two lists of pets.
    List<Pet> pets1 = new List<Pet> { pet1, pet2 };
    List<Pet> pets2 = new List<Pet> { pet1, pet2 };

    bool equal = pets1.SequenceEqual(pets2);

    Console.WriteLine(
        "The lists {0} equal.",
        equal ? "are" : "are not");
}

7 Comments

The OP has implemented Equals on an object which contains an array. It is natural to implement GetHashCode on that object as well.
@MichaelLiu Not how I read it. I am not reading an object which contains an array. I read it as objects in the array override equals this.array[n] == otherObject.array[n].
Why would an object in the array have an Equals method that references this.array? That would mean you have an array of objects which in turn contain arrays.
No it does not mean that. You are reading somethig not there. I agree why would an item in an array need to reference the array? There is slick built in method for comparing two arrays that uses default equality comparer of the items in the array.
"I don't agree you should naturally implement GetHashCode on an array" - Dictionary<T, U> behaves very oddly if you don't implement GetHashCode when you override equals, so I really need GetHashCode.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.