17

我有两个字典,其中包含一个字符串键,然后是一个对象。该对象包含五个字段。有没有一种优雅的方法来确保两个字典首先包含相同的键,然后如果这是正确的,每个对象包含相同的五个字段?

这两个字典会有相同的内置哈希码吗?

编辑,似乎不适用于以下代码:

Dictionary<string, MyClass> test1 = new Dictionary<string, MyClass>();
Dictionary<string, MyClass> test2 = new Dictionary<string, MyClass>();

MyClass i = new MyClass("", "", 1, 1, 1, 1);
MyClass j = new MyClass("", "", 1, 1, 1, 1);

test1.Add("1", i);
test2.Add("1", j);

bool equal = test1.OrderBy(r => r.Key).SequenceEqual(test2.OrderBy(r => r.Key));

class MyClass
{
    private string a;
    private string b;
    private long? c;
    private decimal d;
    private decimal e;
    private decimal f;

    public MyClass(string aa, string bb, long? cc, decimal dd, decimal ee, decimal ff)
    {
        a= aa;
        b= bb;
        c= cc;
        d= dd;
        e= ee;
        f= ff;
    }

这返回假?

4

5 回答 5

25

您可以使用

bool dictionariesEqual = 
    dic1.Keys.Count == dic2.Keys.Count &&
    dic1.Keys.All(k => dic2.ContainsKey(k) && object.Equals(dic2[k], dic1[k]));
于 2012-11-20T09:50:40.883 回答
20

首先你必须在你的类中重写EqualsGetHashCode方法,否则将在引用而不是实际值上进行比较。(要覆盖的代码EqualsGetHashCode在最后提供),之后您可以使用:

var result = (dic1 == dic2) || //Reference comparison (if both points to same object)
             (dic1.Count == dic2.Count && !dic1.Except(dic2).Any());

由于返回 Dictionary 中的项目的顺序是未定义的,因此您不能依赖Dictionary.SequenceEqual (没有OrderBy

你可以试试:

Dictionary<string, object> dic1 = new Dictionary<string, object>();
Dictionary<string, object> dic2 = new Dictionary<string, object>();
dic1.Add("Key1", new { Name = "abc", Number = "123", Address = "def", Loc = "xyz" });
dic1.Add("Key2", new { Name = "DEF", Number = "123", Address = "def", Loc = "xyz" });
dic1.Add("Key3", new { Name = "GHI", Number = "123", Address = "def", Loc = "xyz" });
dic1.Add("Key4", new { Name = "JKL", Number = "123", Address = "def", Loc = "xyz" });

dic2.Add("Key1",new { Name = "abc",Number=  "123", Address= "def", Loc="xyz"});
dic2.Add("Key2", new { Name = "DEF", Number = "123", Address = "def", Loc = "xyz" });
dic2.Add("Key3", new { Name = "GHI", Number = "123", Address = "def", Loc = "xyz" });
dic2.Add("Key4", new { Name = "JKL", Number = "123", Address = "def", Loc = "xyz" });


bool result = dic1.SequenceEqual(dic2); //Do not use that

大多数情况下,上述内容会返回true,但由于 的无序性质,不能真正依赖它Dictionary

由于SequenceEqual也会​​比较顺序,所以只靠可能 SequenceEqual错误的。您必须使用OrderBy来订购两个字典,然后使用SequenceEqual如下:

bool result2 = dic1.OrderBy(r=>r.Key).SequenceEqual(dic2.OrderBy(r=>r.Key));

但这将涉及多次迭代,一次用于排序,另一次用于使用SequenceEqual.

覆盖Equals和代码GetHashCode

private class MyClass
{
    private string a;
    private string b;
    private long? c;
    private decimal d;
    private decimal e;
    private decimal f;

    public MyClass(string aa, string bb, long? cc, decimal dd, decimal ee, decimal ff)
    {
        a = aa;
        b = bb;
        c = cc;
        d = dd;
        e = ee;
        f = ff;
    }

    protected bool Equals(MyClass other)
    {
        return string.Equals(a, other.a) && string.Equals(b, other.b) && c == other.c && e == other.e && d == other.d && f == other.f;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((MyClass)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = (a != null ? a.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ (b != null ? b.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ c.GetHashCode();
            hashCode = (hashCode * 397) ^ e.GetHashCode();
            hashCode = (hashCode * 397) ^ d.GetHashCode();
            hashCode = (hashCode * 397) ^ f.GetHashCode();
            return hashCode;
        }
    }
}

您可能还会看到:覆盖 Equals() 和 GetHashCode() 的正确方法

于 2012-11-20T09:45:09.427 回答
1

仅检查引用相等性的内置Equals功能,请参阅SO 上的这个问题。哈希码不能可靠地告诉您两个对象是否相等;总是有哈希冲突的机会。永远不要使用哈希码作为相等测试!Dictionary<T>

我会手动完成:比较两个字典的条目数,遍历一个字典的键值对并检查键是否存在于另一个字典中,然后比较两个字典中的相应对象。编辑:见罗林的回答:)

于 2012-11-20T09:54:10.730 回答
1

这里有几个我认为非常接近的答案,但我认为应该添加一些额外的点,所以我将它们添加为另一个可能的答案。

首先,我会避免使用 SequenceEquals 方法。它是 Enumerable 的扩展方法,并且隐式要求两个集合的顺序相同。字典并不意味着是有序集合,因此使用 SequenceEquals 意味着您必须不必要地遍历两个字典以创建您也不需要的排序/有序中间集合,然后遍历这些集合以比较它们是否相等. 这似乎真的效率低下并且滥用 LINQ,所有这些都是以试图简洁并编写单行解决方案的名义。如果 OP 的“优雅”概念很简洁,我想这会奏效,但似乎很浪费。

另一方面,如果 OP 的“优雅”理念是有效的,那么您可能需要编写更多代码。首先,您应该为您的类重写 Equals 方法或在您的类中实现 IEquatable(例如,请参见此处)。这将允许您比较字典中的值。然后,您可能想要为您的字典实现类似 IEqualityComparer 之类的接口。

然后,两个字典的比较将如下所示。这只是一个快速而肮脏的“餐巾纸背面”示例,因此它不是最佳方法的示例,但它旨在说明一种仅在必要时对字典进行多次迭代并尽快退出的方法发现不等式。

首先是所需的代码:

public class Foo
{
    //members here...
    public override bool Equals(object obj)
    {
        //implementation here
    }
    //You should probably also override GetHashCode to be thorough,
    //but that's an implementation detail...
}

//This method could stand on its own or you could change it to make it 
//part of the implementation of one of the comparison interfaces...
bool DictionariesEqual(Dictionary<K, V> x, Dictionary<K, V> y)
{
    //If we're comparing the same object, it's obviously equal to itself.
    if(x == y)
    {
        return true;
    }
    //Make sure that we don't have null objects because those are
    //definitely not equal.
    if (x == null || y == null)
    {
        return false;
    }
    
    //Stop processing if at any point the dictionaries aren't equal.
    bool result = false;

    //Make sure that the dictionaries have the same count.
    result = x.Count == y.Count;
    
    //If we passed that check, keep going.
    if(result)
    {
        foreach(KeyValuePair<K, V> xKvp in x)
        {
            //If we don't have a key from one in the other, even though
            //the counts are the same, the dictionaries aren't equal so
            //we can fail out.
            V yValue;
            if(!y.TryGetValue(xKvp.Key, out yValue))
            {
                result = false;
                break;
            }
            else
            {
                //Use the override of the Equals method for your object
                //to see if the value from y is equal to the value from
                //x.
                result = xKvp.Value.Equals(yValue);
                if(!result)
                {
                    //If they're not equal we can just quit out.
                    break;
                }
            }
        }
    }
    return result;
}

然后我们会这样使用它:

Dictionary<string, Foo> dict1 = new Dictionary<string, Foo>();
Dictionary<string, Foo> dict2 = new Dictionary<string, Foo>();
//Fill the dictionaries here...

//Compare the dictionaries
bool areDictsEqual = DictionariesEqual(dict1, dict2);

所以,它不是最简洁的代码,但它也没有超出必要的迭代。在我看来,这更优雅。

编辑:一位评论者建议更新代码示例以使其通用。我最初只是脱离了原始海报的示例,但是将这个通用化似乎是一个好主意,但有一个警告:注意密钥的类型是什么。如果它是 C# 包含相等实现的字符串或 int 之类的简单类型,那么一切都应该很好。如果键是自定义类型,您需要确保覆盖Equals()GetHashCode()为您的自定义类型。

于 2015-08-25T21:11:19.950 回答
0

在这种情况下,您可以只使用 SequenceEquals() 方法,如下所示:

   Dictionary<string, object> d1 = new Dictionary<string, object>();
   d1.Add("first", new { Name = "TestName", Age = 12, ID = 001 }); 

   Dictionary<string, object> d2 = new Dictionary<string, object>();
   d2.Add("first", new { Name = "TestName", Age = 12, ID = 001 });

   Console.WriteLine(d1.SequenceEqual(d2)); //outputs True                

注意:为简单起见,我使用隐式类来填充字典。代码将以相同的方式处理任何对象。两个字典的哈希码不相等,可以通过以下操作轻松验证:

   Console.WriteLine(d1.GetHashCode() + " " + d2.GetHashCode()); //outputs different hashcodes
于 2012-11-20T09:53:29.243 回答