Delphi 2009将GetHashCode函数添加到TObject。 GetHashCode返回一个Integer,用于在TDictionary中进行散列。
如果你想让一个对象在TDictionary中运行良好,你需要适当地重写GetHashCode,这样一般来说,不同的对象会返回不同的整数哈希码。
但是你对包含双字段的对象做了什么?如何将这些double值转换为GetHashCode的整数?
通常用Java完成的方法是使用Double.doubleToLongBits或Float.floatToIntBits之类的方法。后者的文档描述如下:“根据IEEE 754浮点”单格式“位布局返回指定浮点值的表示。”这涉及对浮点值的不同位使用不同掩码的一些按位运算。
Delphi中有没有这样做的功能?
答案 0 :(得分:5)
我建议对Gamecat代码进行以下改进:
type
TVarRec = record
case Integer of
0: ( FInt1, FInt2 : Integer; )
1: ( FDouble : Double; )
end;
function Convert(const ADouble: Double): Integer;
var
arec : TVarRec;
begin
arec.FDouble := ADouble;
Result := arec.FInt1 xor arec.FInt2;
end;
这会考虑Double值的所有位。
(评论不适用于代码)
答案 1 :(得分:2)
如果要将double映射到整数,可以使用变体记录:
type
TVarRec = record
case Integer of
0: ( FInt : Integer; )
1: ( FDouble : Double; )
end;
function Convert(const ADouble: Double): Integer;
var
arec : TVarRec;
begin
arec.FDouble := ADouble;
Result := arec.FInt;
end;
请注意,如果没有对值的解释,这会执行按位复制。
另一种(一种肮脏的技巧,是使用绝对变量:
function Convert(const ADouble: Double): Integer;
var
tempDouble : Double;
tempInt : Integer absolute tempDouble; // tempInt is at the same memory position as tempDouble.
begin
tempDouble := ADouble;
Result := tempInt;
end;
答案 2 :(得分:0)
实际上没有必要做这样的事情,因为GetHashCode的默认值已经返回了一个对于每个对象保证唯一的数字:对象的内存地址。此外,如果更改对象包含的数据,则默认哈希值不会更改。
假设您有一个包含值为3.5的Double的对象,并将其哈希并将其放入字典中,并获得一个哈希代码12345678.您还有其他内容包含对它的引用,并且Double字段被更改,现在它的值为5.21。下次尝试计算其哈希值时,您的哈希码现在为23456789,您的查找将失败。
除非您能保证永远不会发生这种情况,并且您有充分的理由不使用内存地址,否则最好的办法就是将GetHashCode保留原样。 (如果没有破坏,请不要修理它。)
答案 3 :(得分:0)
我想Java可以在Delphi中实现,如下所示:
type
TVarRec = record
case Integer of
0: ( FInt1: Integer; )
1: ( FSingle: Single; )
end;
function GetHashCode(Value: Double): Integer;
var
arec: TVarRec;
begin
arec.FSingle := Value;
Result := arec.FInt1;
end;
背后的想法是降低Double值的精度以匹配Integer的二进制大小(Sizeof(Single)= Sizeof(Integer))。如果您的值可以在没有冲突的情况下以单精度表示,则这将提供良好的哈希值。
编辑:由于类型转换不能在我的D2009中编译,我改编了变体记录解决方案。
答案 4 :(得分:0)
对Double数据使用CRC32,因为 xor 是邪恶的。
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TVarRec = record
case Integer of
0: ( FInt1, FInt2 : Integer; );
1: ( FDouble : Double; );
end;
function Convert(const ADouble: Double): Integer;
var
arec : TVarRec;
begin
arec.FDouble := ADouble;
Result := arec.FInt1 xor arec.FInt2;
end;
var
FDoubleVar1, FDoubleVar2: TVarRec;
HashCode1, HashCode2: Integer;
begin
// Make a Double
FDoubleVar1.FInt1 := $DEADC0DE;
FDoubleVar1.FInt2 := $0C0DEF00;
// Make another Double
FDoubleVar2.FInt1 := $0C0DEF00;
FDoubleVar2.FInt2 := $DEADC0DE;
WriteLn('1rst Double : ', FDoubleVar1.FDouble);
WriteLn('2nd Double : ', FDoubleVar2.FDouble);
HashCode1 := Convert(FDoubleVar1.FDouble);
HashCode2 := Convert(FDoubleVar2.FDouble);
WriteLn('1rst HashCode : ', HashCode1);
WriteLn('2nd HashCode : ', HashCode2);
if HashCode1 = HashCode2 then
begin
WriteLn('Warning: Same HashCode!');
end;
ReadLn;
end.