Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# HashCode Builder

Tags:

java

c#

I used to use the apache hashcode builder a lot

Does this exist for C#

like image 398
Jack Kada Avatar asked May 26 '10 11:05

Jack Kada


3 Answers

This is my homemade builder.

Usage:

hash = new HashCodeBuilder().
             Add(a).
             Add(b).
             Add(c).
             Add(d).
             GetHashCode();

It does not matter what type fields a,b,c and d are, easy to extend, no need to create array.

Source:

public sealed class HashCodeBuilder
{
    private int hash = 17;

    public HashCodeBuilder Add(int value)
    {
        unchecked
        {
            hash = hash * 31 + value; //see Effective Java for reasoning
             // can be any prime but hash * 31 can be opimised by VM to hash << 5 - hash
        }
        return this;
    }

    public HashCodeBuilder Add(object value)
    {
        return Add(value != null ? value.GetHashCode() : 0);
    }

    public HashCodeBuilder Add(float value)
    {
        return Add(value.GetHashCode());
    }

    public HashCodeBuilder Add(double value)
    {
        return Add(value.GetHashCode());
    }

    public override int GetHashCode()
    {
        return hash;
    }
}

Sample usage:

public sealed class Point
{
    private readonly int _x;
    private readonly int _y;
    private readonly int _hash;

    public Point(int x, int y)
    {
        _x = x;
        _y = y;
        _hash = new HashCodeBuilder().
            Add(_x).
            Add(_y).
            GetHashCode();
    }

    public int X
    {
        get { return _x; }
    }

    public int Y
    {
        get { return _y; }
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Point);
    }

    public bool Equals(Point other)
    {
        if (other == null) return false;
        return (other._x == _x) && (other._y == _y);
    }

    public override int GetHashCode()
    {
        return _hash;
    }
}
like image 102
weston Avatar answered Sep 18 '22 22:09

weston


I use the following:

public static int ComputeHashFrom(params object[] obj) {
    ulong res = 0;
    for(uint i=0;i<obj.Length;i++) {
        object val = obj[i];
        res += val == null ? i : (ulong)val.GetHashCode() * (1 + 2 * i);
    }
    return (int)(uint)(res ^ (res >> 32));
}

Using such a helper is quick, easy and reliable, but it has potential two downsides (which you aren't likely to encounter frequently, but are good to be aware of):

  • It can generate poor hashcodes for some distributions of params. For instance, for any int x, ComputeHashFrom(x*-3, x) == 0 - so if your objects have certain pathological properties you may get many hash code collisions resulting in poorly performing Dictionaries and HashSets. It's not likely to happen, but a type-aware hash code computation can avoid such problems more easily.
  • The computation of the hashcode is slower than a specialized computation could be. In particular, it involved the allocation of the params array and a loop - which quite a bit of unnecessary overhead if you've just got two members to process.

Neither of the drawbacks causes any errors merely inefficiency; and both with show up in a profiler as blips in either this method or in the internals of the hash-code consumer.

like image 25
Eamon Nerbonne Avatar answered Sep 21 '22 22:09

Eamon Nerbonne


C# doesn't have a built-in HashCode builder, but you can roll your own. I recently had this precise problem and created this hashcode generator that doesn't use boxing, by using generics, and implements a modified FNV algorithm for generating the specific hash. But you could use any algorithm you'd like, like one of those in System.Security.Cryptography.

    public static int GetHashCode<T>(params T[] args)
    {
        return args.GetArrayHashCode();
    }

    public static int GetArrayHashCode<T>(this T[] objects)
    {
        int[] data = new int[objects.Length];

        for (int i = 0; i < objects.Length; i++)
        {
            T obj = objects[i];
            data[i] = obj == null ? 1 : obj.GetHashCode();
        }

        return GetFnvHash(data);
    }

    private static int GetFnvHash(int[] data)
    {
        unchecked
        {
            const int p = 16777619;
            long hash = 2166136261;

            for (int i = 0; i < data.Length; i++)
            {
                hash = (hash ^ data[i]) * p;
            }

            hash += hash << 13;
            hash ^= hash >> 7;
            hash += hash << 3;
            hash ^= hash >> 17;
            hash += hash << 5;

            return (int)hash;
        }
    }
like image 36
Marcel Valdez Orozco Avatar answered Sep 18 '22 22:09

Marcel Valdez Orozco