【问题标题】:Get last element in a SortedDictionary获取 SortedDictionary 中的最后一个元素
【发布时间】:2009-10-23 12:04:32
【问题描述】:

我看到this question

如何在 .Net 3.5 中获取 SortedDictionary 中的最后一个元素。

【问题讨论】:

    标签: c# collections sorteddictionary


    【解决方案1】:

    您可以使用 LINQ:

    var lastItem = sortedDict.Values.Last();
    

    你也可以得到最后一个key:

    var lastkey = sortedDict.Keys.Last();
    

    你甚至可以得到最后一个键值对:

    var lastKeyValuePair = sortedDict.Last();
    

    这将为您提供具有KeyValue 属性的KeyValuePair<TKey, TValue>

    请注意,如果字典为空,这将引发异常;如果您不想这样,请致电LastOrDefault

    【讨论】:

    • 这些方法可能会触发枚举。我想知道是否有任何方法可以在不枚举的情况下获取最后一个元素(或任何位置索引中的元素)?既然 SortedDictionary 是排序成树的,那理论上是可能的吗?
    • @RolandPihlakas:理论上是的。在实践中,我不这么认为。
    • 对于有 C++ 背景的人来说,这很难接受。枚举整个排序字典只是为了获取最后一个元素是非常低效的。周围有更强大的 C# Collection 库吗?
    • @ThunderGr:不;它没有索引器。
    • @Lander:一旦你删除一个元素,它就会完全崩溃。
    【解决方案2】:

    Last 扩展方法会给你结果,但它必须枚举整个集合才能到达那里。真可惜SortedDictionary<K, V> 没有公开MinMax 成员,特别是考虑到在内部它由具有MinMax 属性的SortedSet<KeyValuePair<K, V>> 支持。

    如果 O(n) 不可取,您有几个选择:

    1. 切换到SortedList<K, V>。再次出于某种原因,BCL 默认情况下不会打包。您可以使用索引器在 O(1) 时间内获取最大值(或最小值)。使用扩展方法进行扩展会很好。

      //Ensure you dont call Min Linq extension method.
      public KeyValuePair<K, V> Min<K, V>(this SortedList<K, V> dict)
      {
          return new KeyValuePair<K, V>(dict.Keys[0], dict.Values[0]); //is O(1)
      }
      
      //Ensure you dont call Max Linq extension method.
      public KeyValuePair<K, V> Max<K, V>(this SortedList<K, V> dict)
      {
          var index = dict.Count - 1; //O(1) again
          return new KeyValuePair<K, V>(dict.Keys[index], dict.Values[index]);
      }
      

      SortedList&lt;K, V&gt; 带有其他惩罚。所以你可能想看看:What's the difference between SortedList and SortedDictionary?

    2. 编写你自己的SortedDictionary&lt;K, V&gt; 类。这是非常微不足道的。将SortedSet&lt;KeyValuePair&lt;K, V&gt;&gt; 作为内部容器,并根据Key 部分进行比较。比如:

      public class SortedDictionary<K, V> : IDictionary<K, V>
      {
          SortedSet<KeyValuePair<K, V>> set; //initialize with appropriate comparer
      
          public KeyValuePair<K, V> Min { get { return set.Min; } } //O(log n)
          public KeyValuePair<K, V> Max { get { return set.Max; } } //O(log n)
      }
      

      这是 O(log n)。没有记录,但我检查了代码。

    3. 使用精巧的反射来访问作为 SortedDictionary&lt;K, V&gt; 类的私有成员的支持集并调用 MinMax 属性。可以依靠表达式来编译委托并将其缓存以提高性能。这样做是一个非常糟糕的选择。不敢相信我提出了这个建议。

    4. 依赖其他实现,例如。对于TreeDictionary&lt;K, V&gt; from C5。他们有FindMinFindMax both of which are O(log n)

    【讨论】:

    • 可能需要重新排序这些选项,以便更好的选项位于顶部,而不是让它们进入,我认为是您认为它们的顺序。
    • 第二个选项您将如何实现 indexer/TryGetValue
    • @CodesInChaos 你是对的,这让它毫无用处。 .NET 中的可悲集合没有公开获取实际参考的方法。我应该编辑答案。
    • 它仍然是 GitHub 中的一个未解决问题:SortedDictionary.First performance。似乎出于某种原因,他们不喜欢向现有集合类型添加新功能。
    【解决方案3】:

    您可以使用SortedDictionary.Values.Last();

    或者如果你想要键和值

    SortedDictionary.Last();
    

    【讨论】:

      【解决方案4】:

      SortedList 列表...

      list[ Keys[Keys.Count - 1] ];  // returns the last entry in list
      

      【讨论】:

        【解决方案5】:

        正如人们已经指出 Last 扩展将枚举整个集合,它对性能的影响可能是致命的。 仅从 SortedDict 中删除 10000 个最后的元素,就比对 SortedSet 的类似操作花费了更多的时间。

        1. SortedSet 删除经过的毫秒数:8

        2. SortedDict 删除经过的毫秒数:3697

          // 在下面的代码中,ss 是 SortedSet,sd 是 SortedDictionary,都包含相同的 10000 个元素。

           sw.Start();
           while (ss.Count != 0)
           {
               ss.Remove(ss.Max);
           }
          
           sw.Stop();
           Console.WriteLine("SortedSet Removal Elapsed ms : {0}", sw.ElapsedMilliseconds);
          
           sw.Reset();
          
           sw.Start();
           while (sd.Count != 0)
           {
               sd.Remove(sd.Keys.Last());
           }
          
           sw.Stop();
           Console.WriteLine("Dict Removal Elapsed ms : {0}", sw.ElapsedMilliseconds);
          

        【讨论】:

          猜你喜欢
          • 2012-04-02
          • 1970-01-01
          • 2010-11-30
          • 1970-01-01
          • 2015-12-07
          • 2010-11-04
          • 1970-01-01
          • 2016-11-26
          • 2015-01-11
          相关资源
          最近更新 更多