zoukankan      html  css  js  c++  java
  • 【.net 深呼吸】EqualityComparer——自定义相等比较

    自定义实现两个对象的相等比较,一种方案是重写Object类的Equals方法,很easy,如果相等返回true,不相等就返回false。不过,如果把自定义相等的比较用于泛型集,比如Dictionary、HashSet等,这些集合都有一个共同点——必须标识存储项的唯一性,即每一个子项都有对应的key。

    object.Equals方法是面向Object类型的,如果用于泛型对象,在判断是否相等的过程需要进行大量的装箱/拆箱操作,尤其是复合类型,由于要进行细致的比较,类型转换更为频繁,这样会带来一定量的性能开销,所以,对于泛型集合的相等比较,应该考虑使用 IEqualityComparer<T>,Dotnet类型提供了一个实现了该接口的抽象类——EqualityComparer<T>。

    在实际使用中,不妨直接实现这个抽象类,好处是该抽象类公开了一个静态的Default属性,可以返回平台默认的比较方案。因此,实现该抽象类的好处在于,既可以提供自定义实现,同时也可以保留默认行为。

    我们先来解释一下,为什么在泛型集合中需要用到自定义相等比较。看例子,咱们以常用的Dictionary为例,字典的Key我用一个叫Entity的类来标识,该类定义如下。

        public class Entity
        {
            public int ID { get; set; }
            public string Name { get; set; }
        }

    然后,我们实例化一个字典,并向其中添加两个项。

                IDictionary<Entity, string> dic = new Dictionary<Entity, string>();
    
                dic.Add(new Entity { ID = 1, Name = "小明" }, "C++");
                dic.Add(new Entity { ID = 2, Name = "小王" }, "VB");

    接着,从字典中读出Key为ID = 2 , Name = "小王" 的值。

                Entity findkey = new Entity
                {
                    ID = 2,
                    Name = "小王"
                };
    
                if (dic.ContainsKey(findkey))
                {
                    Console.WriteLine(dic[findkey]);
                }

    在查找时,先实例化一个Entity,然后给ID和Name属性赋上要查找的值,随后调用字典实例的ContainsKey方法判断一下要查找的key是否存在于字典中,如果存在,就输出该key对应的值。

    代码看起来很完美,但一旦运行,你会发现什么都没找到。为啥呢?

    因为该字典存储项的key是我们自定义的Entity类,当我们要从中查找时,是另外实例化了一个Entity对象,并赋了对应的属性值去查找的,可是问题就来了,我们实例化的findkey对象,与存入到字典中的Entity不是同一个对象,虽然它们的属性值相等,但它们引用的不是同一个实例,因为被判定为不相等的对象,故找不到对应的Key。

    这个时候,EqualityComparer就派上用场了,自定义一个类并从它派生,添加自己的代码实现,不管是不是同一个对象实例,只要属性的值相等,则视为相同的key。

        public sealed class CustomEqComparer : EqualityComparer<Entity>
        {
            public override bool Equals(Entity x, Entity y)
            {
                if (x.ID == y.ID && x.Name == y.Name)
                    return true;
                return false;
            }
    
            public override int GetHashCode(Entity obj)
            {
                return obj.ID.GetHashCode();
            }
        }

    实现Equals方法,如果两个对象相等,返回真,否则返回假。GetHashCode方法返回哈希值,算法不应该过于复杂,避免性能开销,只要能够保证相等的两个对象返回相同的哈希值;不相等的对象返回不同的哈希值,这样就可以了。这里直接以ID属性的值为哈希,所以,每个key的ID值不能重复。

    自定义完比较器后,只要在实例化字典实例时传给它的构造函数就可以了。把上面的代码改为:

                CustomEqComparer comp = new CustomEqComparer();
                IDictionary<Entity, string> dic = new Dictionary<Entity, string>(comp);

    现在,再次执行前面的例子,ID=2,Name="小王"的key就可以查找出来了。

    完整的演示代码如下:

        class Program
        {
            static void Main(string[] args)
            {
                CustomEqComparer comp = new CustomEqComparer();
                IDictionary<Entity, string> dic = new Dictionary<Entity, string>(comp);
    
                dic.Add(new Entity { ID = 1, Name = "小明" }, "C++");
                dic.Add(new Entity { ID = 2, Name = "小王" }, "VB");
    
                Entity findkey = new Entity
                {
                    ID = 2,
                    Name = "小王"
                };
    
                if (dic.ContainsKey(findkey))
                {
                    Console.WriteLine(dic[findkey]);
                }
    
    
                Console.Read();
            }
        }
    
        public class Entity
        {
            public int ID { get; set; }
            public string Name { get; set; }
        }
    
        public sealed class CustomEqComparer : EqualityComparer<Entity>
        {
            public override bool Equals(Entity x, Entity y)
            {
                if (x.ID == y.ID && x.Name == y.Name)
                    return true;
                return false;
            }
    
            public override int GetHashCode(Entity obj)
            {
                return obj.ID.GetHashCode();
            }
        }
  • 相关阅读:
    UVA 10462 Is There A Second Way Left?(次小生成树&Prim&Kruskal)题解
    POJ 1679 The Unique MST (次小生成树)题解
    POJ 2373 Dividing the Path (单调队列优化DP)题解
    BZOJ 2709 迷宫花园
    BZOJ 1270 雷涛的小猫
    BZOJ 2834 回家的路
    BZOJ 2506 calc
    BZOJ 3124 直径
    BZOJ 4416 阶乘字符串
    BZOJ 3930 选数
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5700192.html
Copyright © 2011-2022 走看看