我需要使用byte[]
作为Dictionary
中的关键字。由于byte[]
未覆盖默认的GetHashCode
方法,因此包含相同数据的两个单独的byte[]
对象将在字典中使用两个单独的插槽。基本上我想要的是这个:
Dictionary<byte[], string> dict = new Dictionary<byte[], string>();
dict[new byte[] {1,2,3}] = "my string";
string str = dict[new byte[] {1,2,3}];
// I'd like str to be set to "my string" at this point
有一种简单的方法吗?我唯一能想到的就是构建一个包含byte[]
的包装类,并根据GetHashCode
的内容覆盖byte[]
,但这似乎容易出错。
答案 0 :(得分:66)
默认情况下,byte[]
将通过引用进行比较,在这种情况下,这不是您想要的。您需要做的是指定自定义IEqualityComparer<byte[]>
并进行所需的比较。
例如
public class ByteArrayComparer : IEqualityComparer<byte[]> {
public bool Equals(byte[] left, byte[] right) {
if ( left == null || right == null ) {
return left == right;
}
return left.SequenceEqual(right);
}
public int GetHashCode(byte[] key) {
if (key == null)
throw new ArgumentNullException("key");
return key.Sum(b => b);
}
}
然后你可以做
var dict = new Dictionary<byte[], string>(new ByteArrayComparer());
2.0的解决方案
public class ByteArrayComparer : IEqualityComparer<byte[]> {
public bool Equals(byte[] left, byte[] right) {
if ( left == null || right == null ) {
return left == right;
}
if ( left.Length != right.Length ) {
return false;
}
for ( int i= 0; i < left.Length; i++) {
if ( left[i] != right[i] ) {
return false;
}
}
return true;
}
public int GetHashCode(byte[] key) {
if (key == null)
throw new ArgumentNullException("key");
int sum = 0;
foreach ( byte cur in key ) {
sum += cur;
}
return sum;
}
}
答案 1 :(得分:8)
所以,JaredPar的答案不是坏,但在某些方面可能会更好。首先,the IEqualityComparer page说“我们建议您从EqualityComparer类派生,而不是实现IEqualityComparer接口。”
其次,GetHashCode的实现应该是 fast 。它用于快速消除明显不同的对象,这显然是浪费时间来运行Equals。所以GetHashCode应该比实际运行Equals快得多。
第三,返回JaredPar所做的字节数组之和,很可能产生冲突 - 如果字节顺序不同,或者相对差异相互抵消等等。
所以我会推荐这样的解决方案:
public class ByteArrayComparer : EqualityComparer<byte[]>
{
public override bool Equals(byte[] first, byte[] second)
{
if (first == null || second == null) {
// null == null returns true.
// non-null == null returns false.
return first == second;
}
if (ReferenceEquals(first, second)) {
return true;
}
if (first.Length != second.Length) {
return false;
}
// Linq extension method is based on IEnumerable, must evaluate every item.
return first.SequenceEqual(second);
}
public override int GetHashCode(byte[] obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
// quick and dirty, instantly identifies obviously different
// arrays as being different
return obj.Length;
}
}
上面,返回obj.Length,真的快速而肮脏,但也容易返回很多冲突。我想我们可以做得更好。
如果要检查所有字节,像JaredPar的答案中那样的事件比简单的字节总和更容易发生冲突。但同样,这将检查所有元素,因此它不会比实际运行Equals更好。你也可以无条件地返回0,并且总是强制使用Equals。
我强调:这比在JaredPar的答案中返回总和更好。并且总是返回0比这更好。返回obj.Length比返回0要好。
// This is not recommended. Performance is too horrible.
public override int GetHashCode(byte[] obj)
{
// Inspired by fletcher checksum. Not fletcher.
if (obj == null) {
throw new ArgumentNullException("obj");
}
int sum = 0;
int sumOfSum = 0;
foreach (var val in obj) {
sum += val; // by default, addition is unchecked. does not throw OverflowException.
sumOfSum += sum;
}
return sum ^ sumOfSum;
}
如果你碰巧知道你用作密钥的byte []数组本身就是加密哈希,那么你可以利用这个假设给你带来好处,只需返回转换为{{1}的前4个字节。 }。对于通用字节数组,它也可能正常工作:
int
答案 2 :(得分:4)
你能否将byte []转换为字符串并将其用作密钥?
类似的东西:
ASCIIEncoding enc = new ASCIIEncoding();
byte[] input;
string demo = new string(enc.GetChars(input));
byte[] decode = enc.GetBytes(demo.ToCharArray());
答案 3 :(得分:3)
你的想法也是我的第一个想法。我不认为这会容易出错。但是如果你不喜欢这个选项,你可以创建一个实现IEqualityComparer的类,并将它的一个实例传递给Dictionary的构造函数。
答案 4 :(得分:3)
using System;
using System.Collections;
using System.Collections.Generic;
[Serializable]
class StructuralEqualityComparer : IEqualityComparer, IEqualityComparer<object>
{
public new bool Equals(object x, object y)
{
var s = x as IStructuralEquatable;
return s == null ? object.Equals(x, y) : s.Equals(y, this);
}
public int GetHashCode(object obj)
{
var s = obj as IStructuralEquatable;
return s == null ? EqualityComparer<object>.Default.GetHashCode(obj) : s.GetHashCode(this);
}
}
答案 5 :(得分:1)
通过不在数组上但在IEnumerable<T>
上工作,使EqualityComparer更加通用。
由于事实,我们现在有一个T
,我们需要能够为元素指定一个可选的相等比较器。
最后但并非最不重要的一点是,GetHashCode()
永远不应该抛出,有时你需要快速,有时你需要在第一次运行时更准确。因此,您可以选择从我们自己的哈希值中考虑哈希码的项目数(最大值)来定义准确度。
public class EnumerableEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
private static readonly Lazy<IEqualityComparer<IEnumerable<T>>> Lazy = new Lazy<IEqualityComparer<IEnumerable<T>>>(() => new EnumerableEqualityComparer<T>());
private int accuracy;
private IEqualityComparer<T> comparer;
public EnumerableEqualityComparer()
: this(-1)
{
}
public EnumerableEqualityComparer(int accuracy)
: this(accuracy, null)
{
}
public EnumerableEqualityComparer(IEqualityComparer<T> elementEqualityComparer)
: this(-1, elementEqualityComparer)
{
}
public EnumerableEqualityComparer(int accuracy, IEqualityComparer<T> elementEqualityComparer)
{
if (accuracy < 0)
{
accuracy = 4;
}
this.accuracy = accuracy;
comparer = elementEqualityComparer ?? EqualityComparer<T>.Default;
}
public static IEqualityComparer<IEnumerable<T>> Default { get; private set; } = Lazy.Value;
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
if (ReferenceEquals(x, y))
{
return true;
}
if (ReferenceEquals(x, null)
|| ReferenceEquals(y, null))
{
return false;
}
return x.SequenceEqual(y, comparer);
}
public int GetHashCode(IEnumerable<T> obj)
{
if (ReferenceEquals(obj, null))
{
return -1;
}
var count = (obj as ICollection<T>)?.Count ?? 1;
var hashCode = count * 49297;
foreach (var item in obj.Take(accuracy))
{
hashCode += comparer.GetHashCode(item) * 17123;
}
return hashCode;
}
}
答案 6 :(得分:-3)
当您从Dictionary中检索项目时,您正在使用new运算符作为byte []。这将在字典中查找不存在的不同(新)byte []实例。
这是一个有效的解决方案:
var dict = new Dictionary<byte[], string>();
var b = new byte[] { 1,2,3};
dict[b] = "my string";
var value = dict[b];
Console.WriteLine(value);