通过将4个较小的数字基本类型编码为long (Int64)来生成唯一键

本文关键字:Int64 long 一键 唯一 编码 类型 4个 数字 | 更新日期: 2023-09-27 17:53:42

我有以下方法应该为所提供的参数的任何组合创建一个唯一的long:

private static long GenerateKey(byte sT, byte srcT, int nId, ushort aId)
{
    var nBytes = BitConverter.GetBytes(nId);
    var aBytes = BitConverter.GetBytes(aId);
    var byteArray = new byte[8];
    Buffer.BlockCopy(new[] { sT}, 0, byteArray, 7, 1);
    Buffer.BlockCopy(new[] { srcT}, 0, byteArray, 6, 1);
    Buffer.BlockCopy(aBytes, 0, byteArray, 4, 2);
    Buffer.BlockCopy(nBytes, 0, byteArray, 0, 4);

    var result = BitConverter.ToInt64(byteArray, 0);
    return result;
}

最后是:

   1    2   3   4    5   6   7   8    
------------------------------------
|byte|byte| ushort |      int      |
------------------------------------

这可以用位操作来完成吗?我尝试了下面的方法,但似乎为不同的值生成相同的数字??

var hash = ((byte)sT << 56) ^ ((byte)srcT<< 48) ^ (aId << 32) ^ nId;

通过将4个较小的数字基本类型编码为long (Int64)来生成唯一键

出问题的地方是这样的

((byte)sT << 56)

不是你想要的。它实际做的是将sT转换为byte(它已经是),然后它被隐式转换为int(因为你用它做数学运算),然后它向左移位24(移位计数被屏蔽以限制它们小于左操作数的大小)。

所以你应该转换成宽类型,而不是窄类型,像这样:

((long)sT << 56)

另外,请注意,隐式地将int转换为long符号扩展它,这意味着如果nId是负的,您最终将补充所有其他字段(xor与所有1是互补的)。

那么试试这个:

((long)sT << 56) | ((long)srcT << 48) | ((long)aId << 32) | (nId & 0xffffffffL)

我还把"或"改成了"或",这在组合不重叠的东西时更习惯。

下一行:

var hash = ((byte)sT << 56) ^ ((byte)srcT<< 48) ^ (aId << 32) ^ nId;

你移动的位数超过了类型所能容纳的位数。你不能移动byte超过8位,其余的都丢失了。相反,ushort不能移位超过16位。只需将所有内容转换为long,您将得到预期的结果:

var hash = ((long)sT << 56)
         ^ ((long)srcT << 48)
         ^ ((long)aId << 32)
         ^ (uint)nId; // Cast to uint is required to prevent sign extension