我有一个
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”。存在显式转换(您是否缺少转换?)
假设您的列表已按升序排序
var index = TimeList.BinarySearch(dateTimeOffset);
if (index < 0) index = ~index;
TimeList.Insert(index, dateTimeOffset);
对于边缘情况,@L.B.的答案略有改进:
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);
}
}
SortedSet<T>
,否则您将陷入键值集合SortedList
。
SortedSet<DateTimeOffset> TimeList = new SortedSet<DateTimeOffset>();
// add DateTimeOffsets here, they will be sorted initially
注意:
SortedSet<T>
类不接受重复元素。如果 item 已在集合中,则此方法返回 false 并且不会引发异常。
如果允许重复,您可以使用
List<DateTimeOffset>
并使用它的 Sort
方法。
修改 LINQ,在末尾添加 ToList() :
ItemList = (from s in ItemList
orderby s.PublishDate descending
select s).ToList();
或者将排序后的列表分配给另一个变量
var sortedList = from s in ....
我采用了@Noseratio的答案并进行了修改,并将其与@Jeppe的答案从这里结合起来 获得一个适用于实现 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<T>.Create((x, y) => (x.Property < y.Property) ? -1 : (x.Property > 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);
}
}
我很想对这里的两个建议进行基准测试,使用 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 |
将项目插入特定索引
您可以使用:
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;
非常简单, 将数据添加到列表后
list.OrderBy(a => a.ColumnName).ToList();
使用 Linq,原始列表必须已经排序:
List<DateTimeOffset> timeList = new List<DateTimeOffset>()
{
new DateTimeOffset(2010, 1, 1, 0, 0, 0, TimeSpan.Zero),
new DateTimeOffset(2011, 1, 1, 0, 0, 0, TimeSpan.Zero),
new DateTimeOffset(2012, 1, 1, 0, 0, 0, TimeSpan.Zero),
new DateTimeOffset(2013, 1, 1, 0, 0, 0, TimeSpan.Zero)
};
Console.WriteLine("Original list:");
timeList.ForEach(dto => Console.WriteLine(dto.ToString("yyyy-MM-dd")));
var newDateTimeOffset = new DateTimeOffset(2011, 6, 12, 0, 0, 0, TimeSpan.Zero);
int index = timeList.TakeWhile(dto => DateTimeOffset.Compare(dto, newDateTimeOffset) < 0).Count();
timeList.Insert(index, newDateTimeOffset);
Console.WriteLine("New list:");
timeList.ForEach(dto => Console.WriteLine(dto.ToString("yyyy-MM-dd")));
适用于所有集合,即使它们没有
BinarySearch()
方法。
找到你想要的索引后就可以使用
Insert(index,object)
。