I'm attempting to port some code from Java to C#. In C# I'm using a Dictionary instead of the Java HashMap. However, I'm finding that when I call Dictionary.Add() I'm getting duplicate keys. I suspect this is because the keys are different reference types but the objects should have equality. I've created a minimal example here:
Abstract Class:
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, 0, _cards, 0, prevlen);
_cards[prevlen] = card;
Array.Sort(_cards);
}
}
Extension Class:
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);
}
}
Program:
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);
}
}
}
The output is 2 but I need the keys to be recognised as identical. I think that Dictionary.Add() checks for equality of keys but even so, I'm checking for containsKey before adding the new entry. I suspect there could be a problem with my C# implementation of GetHashCode? The Java version is shown as a comment.
Any ideas?
I think that the problem is that your
GetHashCodedoes not return equal values for equal arrays:This will work:
You also have to fix the logic in
Equals, this is wrong,Array.Equalscompares references:Use this:
I will show you another approach, you can use a custom
IEqualityComparer<T>that works with any kind of sequences(like arrays or lists):With this
EnumerableCompareryou can also "feed" yourNonFinalHandState: