【问题标题】:How to insert item into list in order?如何按顺序将项目插入列表?
【发布时间】:2012-08-23 17:20:34
【问题描述】:

我有一个DateTimeOffset 对象列表,我想按顺序将新对象插入到列表中。

List<DateTimeOffset> TimeList = ...
// determine the order before insert or add the new item

抱歉,需要更新我的问题。

List<customizedClass> ItemList = ...
//customizedClass contains DateTimeOffset object and other strings, int, etc.

ItemList.Sort();    // this won't work until set data comparison with DateTimeOffset
ItemList.OrderBy(); // this won't work until set data comparison with DateTimeOffset

另外,DateTimeOffset如何作为.OrderBy()的参数?

我也试过了:

ItemList = from s in ItemList
           orderby s.PublishDate descending    // .PublishDate is type DateTime
           select s;

但是,它会返回此错误消息,

无法将类型“System.Linq.IOrderedEnumerable”隐式转换为“System.Collections.Gerneric.List”。存在显式转换(您是否缺少演员表?)

【问题讨论】:

  • 不能在需要时对列表进行排序或使用 SortedList 吗?
  • List&lt;T&gt;ordered` 集合。你想*排序吗?
  • 你在说什么“订单”?
  • 我希望我的列表按 DateTimeOffset 排序
  • 或者使用不同的集合类型,比如SortedBag&lt;T&gt;:is-there-a-sorted-collection-type-in-net

标签: c# sorting insert


【解决方案1】:

假设您的列表已经按升序排序

var index = TimeList.BinarySearch(dateTimeOffset);
if (index < 0) index = ~index;
TimeList.Insert(index, dateTimeOffset);

【讨论】:

  • 你能解释一下你的代码吗?如果他们不知道如何插入列表,我怀疑他们会知道~index 做了什么。
  • @AshBurlaczenko,你是对的,但问题的上下文似乎在我回答了约 1 小时后发生了变化,我懒得回答了。
  • 来自 MSDN :返回值 排序 List 中项目的从零开始的索引,如果找到项目;否则,负数是下一个大于 item 的元素的索引的按位补码,如果没有更大的元素,则为 Count 的按位补码。
【解决方案2】:

@L.B.'s answer 在边缘情况下的略微改进版本:

public static class ListExt
{
    public static void AddSorted<T>(this List<T> @this, T item) where T: IComparable<T>
    {
        if (@this.Count == 0)
        {
            @this.Add(item);
            return;
        }
        if (@this[@this.Count-1].CompareTo(item) <= 0)
        {
            @this.Add(item);
            return;
        }
        if (@this[0].CompareTo(item) >= 0)
        {
            @this.Insert(0, item);
            return;
        }
        int index = @this.BinarySearch(item);
        if (index < 0) 
            index = ~index;
        @this.Insert(index, item);
    }
}

【讨论】:

  • 这个 sn-p 在我不能使用 SortedSet 并且不得不重复 .Sort() 一个 List 的情况下让我的性能提高了 1000%。
  • 什么是@this
  • @Baruch @this 是一个纯粹的常规变量名,通常用于引用被 C# 扩展方法扩展的对象。虽然 @this 是一个有效的 C# 变量名,但您可以使用其他任何名称,例如list 在这里可能有意义。
  • this 前面的@ 允许您使用保留字作为参数/变量,供以前没有遇到过的人使用。
【解决方案3】:

使用 .NET 4,您可以使用新的 SortedSet&lt;T&gt;,否则您将无法使用键值集合 SortedList

SortedSet<DateTimeOffset> TimeList = new SortedSet<DateTimeOffset>();
// add DateTimeOffsets here, they will be sorted initially

注意:SortedSet&lt;T&gt; 类不接受重复元素。如果 item 已经在集合中,则此方法返回 false 并且不会抛出异常。

如果允许重复,您可以使用 List&lt;DateTimeOffset&gt; 并使用它的 Sort 方法。

【讨论】:

    【解决方案4】:

    修改你的 LINQ,在末尾添加 ToList():

    ItemList = (from s in ItemList
                orderby s.PublishDate descending   
                select s).ToList();
    

    或者将排序后的列表分配给另一个变量

    var sortedList = from s in ....
    

    【讨论】:

      【解决方案5】:

      我接受了@Noseratio's answer 并对其进行了重新设计,并将其与@Jeppe 来自here 的回答结合起来 获得一个适用于实现 IList 的集合的函数(我需要它用于路径的 ObservableCollection)和不实现 IComparable 的类型。

          /// <summary>
          /// Inserts a new value into a sorted collection.
          /// </summary>
          /// <typeparam name="T">The type of collection values, where the type implements IComparable of itself</typeparam>
          /// <param name="collection">The source collection</param>
          /// <param name="item">The item being inserted</param>
          public static void InsertSorted<T>(this IList<T> collection, T item)
              where T : IComparable<T>
          {
              InsertSorted(collection, item, Comparer<T>.Create((x, y) => x.CompareTo(y)));
          }
      
          /// <summary>
          /// Inserts a new value into a sorted collection.
          /// </summary>
          /// <typeparam name="T">The type of collection values</typeparam>
          /// <param name="collection">The source collection</param>
          /// <param name="item">The item being inserted</param>
          /// <param name="comparerFunction">An IComparer to comparer T values, e.g. Comparer&lt;T&gt;.Create((x, y) =&gt; (x.Property &lt; y.Property) ? -1 : (x.Property &gt; y.Property) ? 1 : 0)</param>
          public static void InsertSorted<T>(this IList<T> collection, T item, IComparer<T> comparerFunction)
          {
              if (collection.Count == 0)
              {
                  // Simple add
                  collection.Add(item);
              }
              else if (comparerFunction.Compare(item, collection[collection.Count - 1]) >= 0)
              {
                  // Add to the end as the item being added is greater than the last item by comparison.
                  collection.Add(item);
              }
              else if (comparerFunction.Compare(item, collection[0]) <= 0)
              {
                  // Add to the front as the item being added is less than the first item by comparison.
                  collection.Insert(0, item);
              }
              else
              {
                  // Otherwise, search for the place to insert.
                  int index = 0;
                  if (collection is List<T> list)
                  {
                      index = list.BinarySearch(item, comparerFunction);
                  }
                  else if (collection is T[] arr)
                  {
                      index = Array.BinarySearch(arr, item, comparerFunction);
                  }
                  else
                  {
                      for (int i = 0; i < collection.Count; i++)
                      {
                          if (comparerFunction.Compare(collection[i], item) <= 0)
                          {
                              // If the item is the same or before, then the insertion point is here.
                              index = i;
                              break;
                          }
      
                          // Otherwise loop. We're already tested the last element for greater than count.
                      }
                  }
      
                  if (index < 0)
                  {
                      // The zero-based index of item if item is found,
                      // otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
                      index = ~index;
                  }
      
                  collection.Insert(index, item);
              }
          }
      

      【讨论】:

      • collection.ToArray() 将创建另一个比线性搜索更昂贵的集合,即collection.IndexOf()
      • 我在最后用新的处理方式进行了编辑——遗憾的是,Collection 没有二进制搜索,但是......嗯。
      【解决方案6】:

      我很想对这里的两个建议进行基准测试,使用 SortedSet 类与基于列表的二进制搜索插入。从我在 .NET Core 3.1 上的(非科学)结果来看,对于小型(数百个)集合,List 似乎可能使用更少的内存,但是随着集合变得越大,SortedSet 在时间和内存上都开始获胜。

      (项目是小类的实例,有两个字段,Guid id 和字符串名称)

      50 项:

      |        Method |     Mean |     Error |    StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
      |-------------- |---------:|----------:|----------:|-------:|------:|------:|----------:|
      |     SortedSet | 5.617 μs | 0.0183 μs | 0.0153 μs | 0.3052 |     - |     - |    1.9 KB |
      | SortedAddList | 5.634 μs | 0.0144 μs | 0.0135 μs | 0.1755 |     - |     - |   1.12 KB |
      

      200 项:

      |        Method |     Mean |    Error |   StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
      |-------------- |---------:|---------:|---------:|-------:|------:|------:|----------:|
      |     SortedSet | 24.15 μs | 0.066 μs | 0.055 μs | 0.6409 |     - |     - |   4.11 KB |
      | SortedAddList | 28.14 μs | 0.060 μs | 0.053 μs | 0.6714 |     - |     - |   4.16 KB |
      

      1000 项:

      |        Method |     Mean |   Error |  StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
      |-------------- |---------:|--------:|--------:|-------:|------:|------:|----------:|
      |     SortedSet | 107.5 μs | 0.34 μs | 0.30 μs | 0.7324 |     - |     - |   4.73 KB |
      | SortedAddList | 169.1 μs | 0.41 μs | 0.39 μs | 2.4414 |     - |     - |  16.21 KB |
      

      【讨论】:

        【解决方案7】:

        将项目插入特定索引

        你可以使用:

        DateTimeOffset dto;
        
         // Current time
         dto = DateTimeOffset.Now;
        
        //This will insert the item at first position
        TimeList.Insert(0,dto);
        
        //This will insert the item at last position
        TimeList.Add(dto);
        

        要对集合进行排序,您可以使用 linq:

        //This will sort the collection in ascending order
        List<DateTimeOffset> SortedCollection=from dt in TimeList select dt order by dt;
        

        【讨论】:

        • 为什么不使用扩展名.OrderBy()进行排序
        • 是的,Ash Burlaczenko 这也是我们可以做到的。我习惯在 linq 中编写大查询。这就是为什么我写了上面的查询,这是我想到的第一个想法。但我同意你的看法。谢谢。
        • List&lt;T&gt;.Add 没有采用索引的重载。我想你的意思是List&lt;T&gt;.Insert
        • 是的,Daniel hilgarth,我的意思是 Insert(Index,object) 方法。谢谢
        • 好问题,杰瑞。我不确定最好的方法。我认为这将取决于我们收集的数据。以及我们插入的日期。但是是的,BinarySearch 非常有效,您可以继续使用该集合或 SortedSet 集合。
        【解决方案8】:

        很简单, 将数据添加到列表后

        list.OrderBy(a => a.ColumnName).ToList();
        

        【讨论】:

          【解决方案9】:

          找到你想要的索引后可以使用Insert(index,object)

          【讨论】:

          • 告诉我排序顺序以写更多
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-08-22
          • 1970-01-01
          • 2012-10-21
          • 1970-01-01
          • 2023-03-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多