将重复的键添加到我的字典中

问题描述

我正在尝试将一些代码从 Java 移植到 C#。在 C# 中,我使用的是字典而不是 Java HashMap。但是,我发现当我调用 Dictionary.Add() 时,我得到了重复的键。我怀疑这是因为键是不同的引用类型,但对象应该具有相等性。我在这里创建了一个最小的例子:

抽象类:

using System;
using System.Text;

public abstract class HandState
{
    public int[] _cards;       // ranks only 
    public int _index;     // into _allStates array
    public char _levelIndex;   // into _levelArrays[n]
    private static String _cardValues = "23456789TJQKA";
    private static int SUIT_COUNT = 4;

    public abstract bool IsFinal();
    public abstract char Value();  // the generated array value

    public override String ToString()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("[");
        for (int i = 0; i < _cards.Length; i++)
        {
            int card = _cards[i];
            sb.Append(_cardValues[card]);
            if (i != _cards.Length - 1)
                sb.Append(" ");
        }
        sb.Append("]");
        return sb.ToString();
    }
    public bool LegalTransistion(int card)
    {
        int rankCount = 0;
        for (int i = 0; i < _cards.Length; i++)
        {
            if (_cards[i] == card)
            {
                rankCount++;
            }
        }
        return rankCount != SUIT_COUNT;
    }
    public void Init(HandState prev,int card)
    {
        int prevlen = prev._cards.Length;
        _cards = new int[prevlen + 1];
        Array.copy(prev._cards,_cards,prevlen);
        _cards[prevlen] = card;
        Array.sort(_cards);
    }
}

扩展类:

using System;
using System.Collections;
using System.Collections.Generic;

public class NonFinalHandState : HandState //extends
{
    public HandState[] _transistions;

    private static List<HandState> _allStatesvec = new List<HandState>();
    private static int RANK_SIZE = 13;
    public NonFinalHandState(int[] cards)
    {
        _cards = (int[])cards.Clone();
    }
    public NonFinalHandState(NonFinalHandState prev,int card)
    {
        Init(prev,card);
    }
    public NonFinalHandState()
    {
        _cards = new int[0];
        _index = _allStatesvec.Count;
        _transistions = new HandState[RANK_SIZE];
        _allStatesvec.Add(this);
    }
    public override bool IsFinal()
    {
        return false;
    }
    public override char Value()
    {
        return _levelIndex;
    }
    public override bool Equals(Object o)
    {
        if (!(o.GetType() == typeof(NonFinalHandState)))
            return false;
        NonFinalHandState other = (NonFinalHandState)o;
        return Array.Equals(_cards,other._cards);
    }
    public override int GetHashCode()
    {
        return ((IStructuralEquatable)_cards).GetHashCode(EqualityComparer<int>.Default); //JAVA: Arrays.hashCode(_cards);
    }
}

程序:

using System;
using System.Collections.Generic;

namespace ConsoleAppTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] cards = { 1,2,3,4 };
            NonFinalHandState nfhs1 = new NonFinalHandState(cards);
            NonFinalHandState nfhs2 = new NonFinalHandState(cards);

            Dictionary<HandState,HandState> dictionary = new Dictionary<HandState,HandState>();
            dictionary.Add(nfhs1,nfhs1);
            //dictionary.Add(nfhs2,nfhs2);

            if (!dictionary.ContainsKey(nfhs2))
            {
                dictionary.Add(nfhs2,nfhs2);
            }
            else
            {
                dictionary[nfhs2] = nfhs2;
            }

            Console.WriteLine(dictionary.Keys.Count);

        }
    }
}

输出为 2,但我需要将密钥识别为相同。我认为 Dictionary.Add() 会检查键的相等性,但即便如此,我还是会在添加新条目之前检查 containsKey。我怀疑我的 GetHashCode 的 C# 实现可能有问题? Java 版本显示为注释。

有什么想法吗?

解决方法

我认为问题在于您的 GetHashCode 不会为相等的数组返回相等的值:

public override int GetHashCode()
{
    return ((IStructuralEquatable)_cards).GetHashCode(EqualityComparer<int>.Default); //JAVA: Arrays.hashCode(_cards);
}

这会起作用:

public override int GetHashCode()
{
    if(_cards == null) return int.MinValue;
    unchecked 
    {
        int hash = 17;
        foreach(int c in _cards)
            hash = hash * 23 + c.GetHashCode();
        return hash;
    }
}

你还要修正Equals中的逻辑,这是错误的,Array.Equals比较引用:

public override bool Equals(Object o)
{
    if (!(o.GetType() == typeof(NonFinalHandState)))
        return false;
    NonFinalHandState other = (NonFinalHandState)o;
    return Array.Equals(_cards,other._cards);
}

使用这个:

public override bool Equals(Object o)
{
    NonFinalHandState other = o as NonFinalHandState;
    if(other == null) return false;
    if(this._cards?.Equals(other._cards) == true) return true; // reference comparison
    if(_cards == null || other._cards == null) return false;
    return _cards.SequenceEqual(other._cards); // add using System.Linq
}

我将向您展示另一种方法,您可以使用自定义 IEqualityComparer<T> 来处理任何类型的序列(如数组或列表):

public class EnumerableComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    private IEqualityComparer<T> m_comparer;

    public EnumerableComparer()
    {
        m_comparer = EqualityComparer<T>.Default;
    }

    public EnumerableComparer(IEqualityComparer<T> comparer)
    {
        m_comparer = comparer ?? EqualityComparer<T>.Default;
    }

    public bool Equals(IEnumerable<T> x,IEnumerable<T> y)
    {
        return Object.ReferenceEquals(x,y) || (x != null && y != null && x.SequenceEqual(y,m_comparer));
    }

    public int GetHashCode(IEnumerable<T> items)
    {
        if(items == null) return 0;
        unchecked
        {
            int hash = 17;
            foreach (T item in items)
                hash = hash * 23 + m_comparer.GetHashCode(t);
            return hash;
        }
    }
}

使用此 EnumerableComparer,您还可以“喂食”您的 NonFinalHandState

public class NonFinalHandState : HandState //extends
{
    private EnumerableComparer<int> _cardsComparer = new EnumerableComparer<int>();
    // ...
    public override bool Equals(Object o) => o is NonFinalHandState nfhs2 && _cardsComparer.Equals(_cards,nfhs2._cards);
    public override int GetHashCode() => _cardsComparer.GetHashCode(_cards);
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...