什么是在.net 2中生成唯一集的最快方法

时间:2008-10-24 10:19:35

标签: c# .net performance collections

我本质上是一个参差不齐的名称值对数组 - 我需要从中生成一组唯一的名称值。锯齿状阵列约为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;

6 个答案:

答案 0 :(得分:12)

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

问题在于比较KeyValuePair结构。 我通过编写一个比较器对象并将其实例传递给Dictionary来解决这个问题。

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

当字典添加(并检查存在)每个项时,它使用Equals和GetHashCode函数,但是当hashcode不那么独特时必须依赖Equals函数。

通过提供更独特的GetHashCode函数,它远远少于Equals函数。我还优化了Equals函数,以便在较少的unqiue键之前比较更独特的值。

使用下面的比较器对象,在0.34秒内运行86,000 * 11个具有10,000个唯一属性的项目(没有比较器对象需要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,其中string = Name + Value),它的速度大约是其两倍。这是一个很好的有趣的问题,我已经花费了很多时间 faaaaaar (虽然我学会了一点安静)

答案 1 :(得分:0)

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

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&amp;之间没有语义关系。你原来有什么......

您可以在问题中提供更多信息吗?

答案 2 :(得分: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;
}

答案 3 :(得分:0)

而不是使用Dictionary为什么不延长KeyedCollection<TKey, TItem>?根据文件:

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

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

答案 4 :(得分: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));

会做到这一点。

答案 5 :(得分:0)

您是否描述过您的代码?您确定foreach循环是瓶颈,而不是retrievever.GetVehicles()?

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

我对第一个键为“0#0”和最后一个“85999#10”的键和值使用了相同的值。

然后我切换到guids。结果相同。

然后我把钥匙做得更长了,就像这样:

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

现在花了差不多10秒钟。

然后我疯狂地做了很长的键并且出现了内存异常。我的计算机上没有交换文件,所以我立即得到了这个例外。

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