6

我有一个本质上是名称值对的锯齿状数组 - 我需要从中生成一组唯一的名称值。锯齿状数组大约有 86,000 x 11 个值。我必须以何种方式存储名称值对(单个字符串“name=value”或专用类,例如 KeyValuePair)对我来说并不重要。
附加信息:有 40 个不同的名称和更多的不同值 - 可能在区域 10,000 个值中。

我正在使用 C# 和 .NET 2.0(性能太差了,我认为将整个锯齿状数组推入 sql 数据库并从那里进行选择可能会更好)。

以下是我正在使用的当前代码:

List<List<KeyValuePair<string,string>>> vehicleList = retriever.GetVehicles();
this.statsLabel.Text = "Unique Vehicles: " + vehicleList.Count;

Dictionary<KeyValuePair<string, string>, int> uniqueProperties = new Dictionary<KeyValuePair<string, string>, int>();
foreach (List<KeyValuePair<string, string>> vehicle in vehicleList)
{
    foreach (KeyValuePair<string, string> property in vehicle)
    {
        if (!uniqueProperties.ContainsKey(property))
        {
            uniqueProperties.Add(property, 0);
        }
    }
}
this.statsLabel.Text += "\rUnique Properties: " + uniqueProperties.Count;
4

6 回答 6

12

我让它在 0.34 秒内运行,从 9 分钟以上

问题在于比较 KeyValuePair 结构时。我通过编写一个比较器对象并将它的一个实例传递给字典来解决它。

据我所知, KeyValuePair.GetHashCode() 返回它的Key对象的哈希码(在这个例子中是最不唯一的对象)。

当字典添加(并检查是否存在)每个项目时,它同时使用 Equals 和 GetHashCode 函数,但当哈希码不那么唯一时必须依赖 Equals 函数。

通过提供更独特的 GetHashCode 函数,它使用 Equals 函数的频率要低得多。我还优化了 Equals 函数,以在不那么唯一的键之前比较更独特的值。

86,000 * 11 个具有 10,000 个独特属性的项目使用下面的比较器对象在 0.34 秒内运行(没有比较器对象需要 9 分 22 秒)

希望这可以帮助 :)

    class StringPairComparer
        : IEqualityComparer<KeyValuePair<string, string>>
    {
        public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
        {
            return x.Value == y.Value && x.Key == y.Key;
        }
        public int GetHashCode(KeyValuePair<string, string> obj)
        {
            return (obj.Key + obj.Value).GetHashCode();
        }
    }

编辑:如果它只是一个字符串(而不是 KeyValuePair,其中字符串 = 名称 + 值),它将快两倍。这是一个很好的有趣的问题,我花了太多时间在上面(不过我学会了安静一点)

于 2008-10-30T20:21:29.797 回答
0

如果您不需要每个键/值对与您生成的唯一值之间的任何特定关联,您可以只使用 GUID?我假设问题是您当前的“密钥”在这个锯齿状数组中不是唯一的。

Dictionary<System.Guid, KeyValuePair<string, string>> myDict 
   = new Dictionary<Guid, KeyValuePair<string, string>>();


foreach of your key values in their current format
   myDict.Add(System.Guid.NewGuid(), new KeyValuePair<string, string>(yourKey, yourvalue))

听起来它会存储你需要的东西,但我不知道你将如何从中提取数据,因为生成的 Guid 和你最初拥有的东西之间没有语义关系......

你能在你的问题中提供更多信息吗?

于 2008-10-24T10:25:44.080 回答
0

使用 KeyValuePair 作为包装类,然后创建一个字典来创建一个集合?或者实现您自己的覆盖 Equals 和 GetHashCode 的包装器。

Dictionary<KeyValuePair, bool> mySet;

for(int i = 0; i < keys.length; ++i)
{
    KeyValuePair kvp = new KeyValuePair(keys[i], values[i]);
    mySet[kvp] = true;
}
于 2008-10-24T10:35:29.787 回答
0

而不是使用Dictionary为什么不扩展KeyedCollection<TKey, TItem>?根据文档:

为其键嵌入值中的集合提供抽象基类。

然后,您需要覆盖该protected TKey GetKeyForItem(TItem item)功能。因为它是 和 之间的混合体IList<T>IDictionary<TKey, TValue>我认为它可能会很快。

于 2008-10-24T11:11:43.330 回答
0

怎么样:

Dictionary<NameValuePair,int> hs = new Dictionary<NameValuePair,int>();
foreach (i in jaggedArray)
{
    foreach (j in i)
    {
        if (!hs.ContainsKey(j))
        {
            hs.Add(j, 0);
        }
    }
}
IEnumerable<NameValuePair> unique = hs.Keys;

当然,如果您使用的是 C# 3.0、.NET 3.5:

var hs = new HashSet<NameValuePair>();
hs.UnionWith(jaggedArray.SelectMany(item => item));

会成功的。

于 2008-10-24T11:22:20.237 回答
0

你分析过你的代码吗?您确定 foreach 循环是瓶颈,而不是retriever.GetVehicles()?

我确实创建了一个小型测试项目,在其中伪造了检索器并让它返回 86.000 X 11 值。我的第一次尝试运行了 5 秒,创建了包含的数据。

我对键和值都使用了相同的值,其中第一个键是“0#0”,最后一个键是“85999#10”。

然后我切换到向导。结果相同。

然后我把钥匙变长了,像这样:

        var s = Guid.NewGuid().ToString();
        return s + s + s + s + s + s + s+ s + s + s;

现在花了将近10秒。

然后我把钥匙弄得非常长,并且出现了内存不足的异常。我的计算机上没有交换文件,所以我立即得到了这个异常。

你的钥匙有多长?您的虚拟内存消耗是性能不佳的原因吗?

于 2008-10-25T21:26:53.940 回答