问题是数据结构设计不符合要求:需要为同一个 XPos 存储多个 Headers。因此,SortedList<XPos, value> 的值不应为Header,而应为List<Header>。这是一个简单而小的更改,但它解决了所有问题并避免像其他建议的解决方案一样产生新问题(请参阅下面的说明):
using System;
using System.Collections.Generic;
namespace TrySortedList {
class Program {
class Header {
public int XPos;
public string Name;
}
static void Main(string[] args) {
SortedList<int, List<Header>> sortedHeaders = new SortedList<int,List<Header>>();
add(sortedHeaders, 1, "Header_1");
add(sortedHeaders, 1, "Header_2");
add(sortedHeaders, 2, "Header_3");
foreach (var headersKvp in sortedHeaders) {
foreach (Header header in headersKvp.Value) {
Console.WriteLine(header.XPos + ": " + header.Name);
}
}
}
private static void add(SortedList<int, List<Header>> sortedHeaders, int xPos, string name) {
List<Header> headers;
if (!sortedHeaders.TryGetValue(xPos, out headers)){
headers = new List<Header>();
sortedHeaders[xPos] = headers;
}
headers.Add(new Header { XPos = xPos, Name = name });
}
}
}
Output:
1: Header_1
1: Header_2
2: Header_3
请注意,添加“有趣”键,例如添加随机数或假装具有相同值的 2 个 XPos 不同会导致许多其他问题。例如,删除特定的 Header 变得困难甚至不可能。
另外请注意,如果只需要对少数 List<Header> 进行排序,则排序性能要好于每个 Header。示例:如果有 100 个 XPos,每个都有 100 个标头,则需要对 10000 个Header 进行排序,而不是 100 个List<Header>。
当然,这个方案也有一个缺点:如果有很多 XPos 只有 1 个 Header,那么需要创建很多 List,这是一些开销。
22.12.2021 更新
我终于有时间编写一个名为SortedBucketCollection 的合适集合,它的行为类似于SortedList。它为每个项目使用 2 个键,第一个键与 SortedList 键相同,并且该键的许多项目可以具有相同的值。第二个键用于区分与 key1 共享相同值的项目。 SortedBucketCollection 使用的存储空间比SortedList<int, List<Header>> 少,因为它为每个“桶”使用一个链表而不是List<>。
使用SortedBucketCollection 的代码如下所示:
使用系统;
namespace SortedBucketCollectionDemo {
public record FinanceTransaction
(int No, DateTime Date, string Description, decimal Amount);
class Program {
static void Main(string[] args) {
//Constructing a SortedBucketCollection
var transactions =
new SortedBucketCollection<DateTime, int, FinanceTransaction>
(ft=>ft.Date, ft=>ft.No);
var date1 = DateTime.Now.Date;
//Adding an item to SortedBucketCollection
transactions.Add(new FinanceTransaction(3, date1, "1.1", 1m));
transactions.Add(new FinanceTransaction(1, date1, "1.2", 2m));
transactions.Add(new FinanceTransaction(0, date1, "1.3", 3m));
var date2 = date1.AddDays(-1);
transactions.Add(new FinanceTransaction(1, date2, "2.1", 4m));
transactions.Add(new FinanceTransaction(2, date2, "2.2", 5m));
//Looping over all items in a SortedBucketCollection
Console.WriteLine("foreach over all transactions");
foreach (var transaction in transactions) {
Console.WriteLine(transaction.ToString());
}
//Accessing one particular transaction
var transaction12 = transactions[date1, 1];
//Removing a transaction
transactions.Remove(transaction12!);
//Accessing all items of one day
Console.WriteLine();
Console.WriteLine("foreach over transactions of one day");
Console.WriteLine(date1);
foreach (var transaction in transactions[date1]) {
Console.WriteLine(transaction.ToString());
}
}
}
}
第一个 foreach 的输出:
FinanceTransaction { No = 1, Date = 07.11.2021 00:00:00, Description = 2.1, Amount = 4 }
FinanceTransaction { No = 2, Date = 07.11.2021 00:00:00, Description = 2.2, Amount = 5 }
FinanceTransaction { No = 0, Date = 08.11.2021 00:00:00, Description = 1.3, Amount = 3 }
FinanceTransaction { No = 1, Date = 08.11.2021 00:00:00, Description = 1.2, Amount = 2 }
FinanceTransaction { No = 3, Date = 08.11.2021 00:00:00, Description = 1.1, Amount = 1 }
请注意,项目不是按添加顺序进行迭代,而是按其key1 和key2 排序。
SortedBucketCollection的详细说明及源代码见我在CodeProject上的文章SortedBucketCollection: A memory efficient SortedList accepting multiple items with the same key