过滤字典并在C#中“简化”其值的最快方法

时间:2018-01-16 16:44:16

标签: c# linq dictionary sorteddictionary

在C#中,给定一个SortedDictionary,我需要对其键进行过滤,然后“简化”其值。以下MWE最好地解释了这一点,它完全符合我的要求

static void Main()
{
    var lowerBound = new DateTime(2018, 01, 02);
    var upperBound = new DateTime(2018, 01, 04);

    var myInput = new SortedDictionary<DateTime, SimpleItem>();

    myInput.Add(new DateTime(2018, 01, 01), new SimpleItem { item1 = 1.1, item2 = 2.1 });
    myInput.Add(new DateTime(2018, 01, 02), new SimpleItem { item1 = 1.2, item2 = 2.2 });
    myInput.Add(new DateTime(2018, 01, 03), new SimpleItem { item1 = 1.3, item2 = 2.3 });
    myInput.Add(new DateTime(2018, 01, 04), new SimpleItem { item1 = 1.4, item2 = 2.4 });
    myInput.Add(new DateTime(2018, 01, 05), new SimpleItem { item1 = 1.5, item2 = 2.5 });
    myInput.Add(new DateTime(2018, 01, 06), new SimpleItem { item1 = 1.6, item2 = 2.6 });
    myInput.Add(new DateTime(2018, 01, 07), new SimpleItem { item1 = 1.7, item2 = 2.7 });

    var q = myInput.Where(x => x.Key >= lowerBound && x.Key <= upperBound);

    Dictionary<DateTime, double> d = 
                  q.ToDictionary(x => x.Key, x => x.Value.item1);

    SortedDictionary<DateTime, double> myOutput = 
                  new SortedDictionary<DateTime, double>(d);

    int wait = 0;
}

class SimpleItem
{
    public double item1 { get; set; }
    public double item2 { get; set; }
}

通过分析我的实际代码(不是这个MWE),很明显ToDictionary 非常慢(所有其他部分看起来都没问题)。所以我只是想要另一种方式(希望最快)做同样的事情。

2 个答案:

答案 0 :(得分:0)

SortedDictionary构造函数只是迭代输入字典的KeyValuePair对象并调用.Add()

public SortedDictionary(IDictionary<TKey,TValue> dictionary, IComparer<TKey> comparer) {
    if( dictionary == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
    }

    _set = new TreeSet<KeyValuePair<TKey, TValue>>(new KeyValuePairComparer(comparer));

    foreach(KeyValuePair<TKey, TValue> pair in dictionary) {
        _set.Add(pair);
    }            
}

这意味着您无法通过创建中间词典获得任何收益。您可以编写一个查询来过滤并选择所需的值,并通过ICollection.Add方法将它们添加到字典中:

var q = myInput.Where(x => x.Key >= lowerBound && x.Key <= upperBound)
               .Select(x=>new KeyValuePair<DateTime,double>(x.Key,x.Value.item1));

var myOutput = new SortedDictionary<DateTime, double>();    
var coll=(ICollection<KeyValuePair<DateTime,double>>)myOutput;

foreach(var pair in q)
{
  coll.Add(pair);
}

SortedDictionary对于编写枚举并不是线程安全的,这意味着您无法使用PLINQ来加速过滤源词典或创建新词典。

答案 1 :(得分:0)

您的问题是您对SortedDictionary的过滤没有利用它已排序的事实。由于ICollection(以及一般的C#泛型集合)不实现任何类型的高效拼接操作,因此查找是最好的选择。

转过滤器,你得到:

var q = Enumerable.Range(0, (Int32)(upperBound - lowerBound).TotalDays+1).Select(n => new { Key = lowerBound.AddDays(n), Item = myInput[lowerBound.AddDays(n)].item1 });

var myOutput = new SortedDictionary<DateTime, double>();

foreach (var pair in q)
    myOutput.Add(pair.Key, pair.Item);

其他方法几乎同时平均。在lowerBoundupperBound中使用非常小的分隔可以使性能提高数千倍。当myInput包含200万个条目时,即使使用两年的跨度也会使性能提高数百倍。

请注意,加速范围实际上取决于SortedList中的条目数,小名单在性能方面不会有太大差异。