编辑:64或128位也可以。由于某种原因,我的大脑刚刚跳到32位,认为这就足够了。
我有一个结构,主要由数值(int,decimal)和3个字符串组成,每个字符串不超过12个字母。我正在尝试创建一个整数值,它将作为哈希码,并尝试快速创建它。一些数值也可以为空。
BitVector32或BitArray似乎是在这个项目中使用的有用实体,但我只是不确定如何在这项任务中弯曲它们。我的结构包含3个字符串,12个小数(其中7个可以为空)和4个整数。
为了简化我的用例,假设你有以下结构:
public struct Foo
{
public decimal MyDecimal;
public int? MyInt;
public string Text;
}
我知道我可以为每个值获取数字标识符。从数字的角度来看,MyDecimal和MyInt当然是独一无二的。该字符串有一个GetHashCode()函数,它将返回一个通常唯一的值。
因此,使用每个数字标识符,是否可以生成唯一标识此结构的哈希码?例如我可以比较包含相同值的2个不同的Foo,并且每次都获得相同的哈希码(无论应用域,重新启动应用,时间,木星卫星对齐等)。
哈希是稀疏的,所以我不预期我的用例会发生冲突。
有什么想法吗?我第一次运行它时,我将所有内容转换为字符串表示形式,对其进行了汇总,并使用了内置的GetHashCode(),但这似乎非常低效。
编辑:更多背景信息。结构数据被传递给web客户端,客户端对包含的值,字符串构造等进行大量计算以重新呈现页面。上述19字段结构表示单个信息单元,每个页面可以具有许多单元。我想对渲染结果进行一些客户端缓存,因此如果我从服务器看到相同的哈希标识符,我可以快速重新渲染单元而无需在客户端重新计算。 JavaScript数值都是64位,所以我认为我的32位约束是人为的和限制性的。 64位可以工作,或者我想甚至128位,如果我可以在服务器上将它分成两个64位值。
答案 0 :(得分:3)
那么,即使在稀疏表中,也应该更好地准备碰撞,这取决于“稀疏”的含义。
你需要能够同时对你将要散列的数据进行非常的假设,以便用32位来打败这个图。
使用SHA256。您的哈希值不依赖于CLR版本,您将不会发生冲突。那么,你仍然会有一些,但不像陨石撞击那么频繁,所以你可以负担不起任何预期。
答案 1 :(得分:1)
答案 2 :(得分:1)
根据哈希函数的定义,哈希码并不是唯一的。它们仅用于尽可能均匀地分布在所有结果值上。获取对象的哈希码意味着快速方式来检查两个对象是否不同。如果两个对象的哈希码不同,那么这些对象是不同的。但是如果哈希码是相同的,你必须深入比较对象。散列码的主要用途是在所有基于散列的集合中,它们可以实现几乎O(1)的检索速度。
所以从这个角度来看,你的GetHashCode
并不一定非常复杂,事实上它不应该。它必须在非常快速和产生均匀分布的值之间取得平衡。如果获得哈希码需要很长时间,那么它就会变得毫无意义,因为深度比较的优势已经消失。如果在另一个极端,哈希码总是1
例如(快速点亮)它将导致在每种情况下的深度比较,这使得这个哈希码也没有意义。
所以要保持正确的平衡,不要试图想出一个完美的哈希码。在所有(或大多数)成员上调用GetHashCode
,并使用Xor
运算符将结果合并为按位移位运算符<<
或>>
。框架类型GetHashCode
已经完全优化,尽管在每个应用程序运行中都不能保证它们相同。没有保证,但他们也没有必要改变,很多人没有。使用反射器根据反射的代码确定或创建自己的版本。
在您的特定情况下,通过查看其哈希码来确定您是否已经处理了一个结构,这有点冒险。散列越好风险越小但仍然如此。最终且唯一的唯一哈希码是......数据本身。使用哈希码时,您还必须覆盖Object.Equals
,以使您的代码真正可靠。
答案 3 :(得分:0)
我相信.NET中常用的方法是在结构的每个成员上调用GetHashCode并对结果进行xor。
但是,我不认为GetHashCode声称在不同的应用程序域中为相同的值生成相同的哈希值。
您是否可以在问题中提供更多信息,说明您为何需要此哈希值以及为什么需要在一段时间内保持稳定,不同的应用域等等。
答案 4 :(得分:0)
你追求的目标是什么?如果它是性能,那么你应该使用一个类,因为每当你将结构作为函数参数传递时,结构将被值复制。
3个字符串,12个小数(其中7个可以为空)和4个整数。
在64位机器上,指针的大小为8个字节,十进制为16个字节,int为4个字节。忽略填充结构将使用每个实例232字节。与推荐的最大16个字节相比,这要大得多,因为它的对象标题,类占用了至少16个字节,...)
如果您需要值的指纹,您可以使用加密级哈希算法,如SHA256,它将产生16字节指纹。这仍然不是独一无二的,但至少是足够独特的。但这也会花费相当多的性能。
<强> EDIT1:强> 在您明确表示需要哈希码来识别Java Script Web客户端缓存中的对象之后,我很困惑。为什么服务器再次发送相同的数据?使服务器更智能,只发送客户端尚未收到的数据会不会更简单?
在您的情况下,SHA哈希算法可以创建一些对象实例标记。
为什么你需要一个哈希码呢?如果您的目标是以内存有效的方式存储值,则可以创建一个FooList,它使用字典仅存储相同的值一次,并使用和int作为查找键。
using System;
using System.Collections.Generic;
namespace MemoryEfficientFoo
{
class Foo // This is our data structure
{
public int A;
public string B;
public Decimal C;
}
/// <summary>
/// List which does store Foos with much less memory if many values are equal. You can cut memory consumption by factor 3 or if all values
/// are different you consume 5 times as much memory as if you would store them in a plain list! So beware that this trick
/// might not help in your case. Only if many values are repeated it will save memory.
/// </summary>
class FooList : IEnumerable<Foo>
{
Dictionary<int, string> Index2B = new Dictionary<int, string>();
Dictionary<string, int> B2Index = new Dictionary<string, int>();
Dictionary<int, Decimal> Index2C = new Dictionary<int, decimal>();
Dictionary<Decimal,int> C2Index = new Dictionary<decimal,int>();
struct FooIndex
{
public int A;
public int BIndex;
public int CIndex;
}
// List of foos which do contain only the index values to the dictionaries to lookup the data later.
List<FooIndex> FooValues = new List<FooIndex>();
public void Add(Foo foo)
{
int bIndex;
if(!B2Index.TryGetValue(foo.B, out bIndex))
{
bIndex = B2Index.Count;
B2Index[foo.B] = bIndex;
Index2B[bIndex] = foo.B;
}
int cIndex;
if (!C2Index.TryGetValue(foo.C, out cIndex))
{
cIndex = C2Index.Count;
C2Index[foo.C] = cIndex;
Index2C[cIndex] = cIndex;
}
FooIndex idx = new FooIndex
{
A = foo.A,
BIndex = bIndex,
CIndex = cIndex
};
FooValues.Add(idx);
}
public Foo GetAt(int pos)
{
var idx = FooValues[pos];
return new Foo
{
A = idx.A,
B = Index2B[idx.BIndex],
C = Index2C[idx.CIndex]
};
}
public IEnumerator<Foo> GetEnumerator()
{
for (int i = 0; i < FooValues.Count; i++)
{
yield return GetAt(i);
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
class Program
{
static void Main(string[] args)
{
FooList list = new FooList();
List<Foo> fooList = new List<Foo>();
long before = GC.GetTotalMemory(true);
for (int i = 0; i < 1000 * 1000; i++)
{
list
//fooList
.Add(new Foo
{
A = i,
B = "Hi",
C = i
});
}
long after = GC.GetTotalMemory(true);
Console.WriteLine("Did consume {0:N0}bytes", after - before);
}
}
}
可以找到类似的内存保存列表here