如何从三个值<int, int, DateTime>创建唯一ID?

6
我是一个有用的助手,可以进行文本翻译。以下是需要翻译的内容:

我正在解析一个在线网络数据源(TCP 中继),每秒发送约30-50条信息(300-500行数据)。这些信息包含两种类型: 订单历史数据

因此,对于订单,每个订单都有一个唯一的ID,并且我已经得到了:

private static Dictionary<long,MarketOrder> MarketOrders = new Dictionary<long,MarketOrder>();

我将订单按照它们的顺序插入到系统中。数据来自缓存文件,因此消息可能包含旧数据,需要进行过滤。目前我的做法是:

if (MarketOrders.ContainsKey(order.OrderID))
{
    // If record exists in a dictionary add hits and overwrite the object if newer.
    int hits = MarketOrders[order.OrderID].Hits;

    if (MarketOrders[order.OrderID].LastUpdated < order.LastUpdated)
    {
        MarketOrders[order.OrderID] = order;
    }

    MarketOrders[order.OrderID].Hits = hits + 1;
}
else
{
    // If not, add new one
    order.Hits = 1;
    MarketOrders.Add(order.OrderID, order);
}

这个程序在BackgroundWorker进程中运行,当字典项数达到2500时,使用二进制序列化进行深度克隆,然后清除并启动另一个后台进程,将克隆副本插入到数据库中。一旦字典被清除,订单就会再次插入。因此,我正在尝试尽可能多地接收并分批插入到数据库中。
我试图用类似的方法处理历史数据。没有唯一的ID,唯一性来自<int, int, DateTime>值的组合。
我需要一种快速的方法从这3个值生成一个唯一的键,以便像订单一样存储在字典中,或者使用另一种存储和过滤数据的方法。
有什么建议吗?我针对.NET 4.0。

1
你能否只是创建一个GUID作为唯一标识符,或者必须使用int、int、Datatime来创建它? - sa_ddam213
1
为什么要使用序列化进行深度克隆?你不能为每个批次创建一个新的“字典”吗? - svick
@sa_ddam213 我需要在内存列表/字典和数据库中保持唯一的ID。这样,在插入时就可以检查是否已经存在相同的行。 - LukeP
如果您获得两个具有相同<int,int,DateTime>组合的历史记录条目,它们是相同的历史记录条目吗?还是您收到的所有条目都是唯一的? - tallseth
@tallseth 这将是相同的条目。 - LukeP
显示剩余3条评论
6个回答

6
Dictionary的键不一定是简单类型。在您的情况下,最简单的解决方案是使用Tuple<int, int, DateTime>作为键。另一种方法是创建自定义类型,正确实现Equals()GetHashCode()(最好还要实现IEquatable)。
在数据库端也可以做同样的事情,大多数数据库支持复合键

我喜欢使用元组作为字典键的想法。性能如何?我特别担心在键中传递 DateTime,因为我将会进行大量查找。 - LukeP
2
我认为性能不应该是一个问题,DateTime 内部只是一个 ulong。但是看起来 Tuple.GetHashCode() 的实现似乎会装箱值类型,这意味着自定义类可能更有效率。 - svick

1
你可以创建一个 Guid 并将其用作键:
byte[] bytes = new byte[16];

BitConverter.GetBytes(i1).CopyTo(bytes, 0);
BitConverter.GetBytes(i2).CopyTo(bytes, 4);
BitConverter.GetBytes(dt.Ticks).CopyTo(bytes, 8);

Guid key = new Guid(bytes);

在使用Dictionary<Guid, int>Dictionary<Tuple<int, int, DateTime>, int>时,将其放入循环中运行,Guid键似乎更快,但您应该在您的情况下进行测试。
仅为澄清起见,我使用了Dictionary<Guid, int>进行测试,但在您的情况下,它将是Dictionary<Guid, YourHistoryType>。我不会惊讶,如果在您的代码中有其他事情要处理,那么使用GuidTuple<int, int, DateTime>之间的任何差异都是微不足道的,您可以使用任何一个看起来更合适的。

我会测试并回报结果。谢谢。 - LukeP
我运行了5个测试,每批处理10,000个项目,元组在代码中的平均时间为9.88秒,在数据库中为6.75秒。你的Guid解决方案确实稍微快一点,但正如你所说,差别不大:代码中为8.80秒,在数据库中为6.63秒。它也比元组更易处理。谢谢! - LukeP

0
你认为将所有数据放入数组或其他对象中并进行序列化如何?
此外,您可以使用MD5算法将所有内容打包成固定长度的字符串。

我认为使用CRC不是一个好主意。 CRC用于错误检查,而不是创建唯一值。 - svick
@svick 我知道这是滥用 CRC,但我认为它相对快速,并且可以从相当长的二进制数据提供独特的短字符串。你也认为 MD5 不好吗?你能解释一下原因吗?// 来自波兰的问候 :) - Kamil
重点是CRC是为完全不同的目的而制作的,它甚至不会尝试创建一个唯一的字符串,因此将其用于此目的是一个可怕的想法。MD5更好,因为它应该提供一个唯一的字符串。但我认为这里仍然不是最好的解决方案。 - svick
谢谢。我正在考虑MD5,我添加了CRC作为备选方案。我会修正我的答案并删除CRC。 - Kamil

0

我更喜欢svick的答案,但是为了提供另一种选择,怎么样使用嵌套的Dictionary呢?Dictionary<int, Dictionary<int, Dictionary<DateTime, object>>>。这可行吗?它可能允许快速查找项目集合。


-2

这样怎么样:

int i1 = 123123;
int i2 = 23433;
DateTime dt = DateTime.Now;
string s;
s = i1.ToString("X") + i2.ToString("X") + dt.Ticks.ToString();

1
没有某种分隔符,这个操作将无法正确执行。而且无论如何,这样做都不是一个好的实践,“string”并不是一种适用于所有不适合其他基本类型的“基础类型”。 - svick

-2

在我看来,唯一的方法是这样做:

DateTime dt = GetYourDateTime();
string uniqueID = dt.Year + "" + dt.Month + "" + dt.Day + "" + dt.Hour + "" + dt.Minute + "" + dt.Second + "" + dt.Millisecond + "";

你可以将其转换为任何数字类型,例如十进制、长整型等。

更新

int a = 2000;
int b = 3000;
DateTime dt = GetYourDateTime();
    string uniqueID = a + "-" + b+ "-" +dt.Year + "" + dt.Month + "" + dt.Day + "" + dt.Hour + "" + dt.Minute + "" + dt.Second + "" + dt.Millisecond + "";

2013年1月11日:2000-3000-2013011100000000

2013年11月1日:2000-3000-2013110100000000


我知道这是一种愚蠢的方式来实现某些事情,但也许它会对你有所帮助。 - Abdul Majid
3
这个问题涉及到一个三元组int,int,DateTime,而不仅仅是一个DateTime。我不明白为什么这样一个字符串比直接使用DateTime作为id更好。此外,在你的实现中,1月11日和11月1日将具有相同的id。 - svick
你评论中第一个语句的答案在你的第二个语句中... 但是我已经更新了我的答案,以向你展示完整的演示。 - Abdul Majid
你尝试过实际运行你的代码吗?它不会产生像“20130111”这样的结果,而是“2013111”。如果你将“int”格式化为“string”,默认情况下没有前导零。 - svick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接