排序列表的动态键<键,值>
本文关键字:排序 动态 列表 | 更新日期: 2023-09-27 18:35:16
我有以下课程
public class MyDictionary : SortedList<int, MyData>
{
}
目前,SortedList
中的Key
代表一个年份数字,例如 2014、2015、2016 等。Value
表示一年的数据。
现在我有一个新的要求,说每年有一个Value
是不够的,这个类应该支持更精细的粒度。
新的粒度如下所示:
- 每年
- 季刊
- 每月
- 周刊
- 日常
当然,一个MyDictionary
实例应该代表一个时间框架,例如 SortedList<Yearly, MyData>
,SortedList<Monthly, MyData>
。
进入MyDictionary
的数据跨越了几年。这意味着我不能使用,例如每月粒度中的月数。例:
2014-12
2015-01
2015-02
...
2015-12
如您所见,数字 12 在列表中是两次。
我的问题是,我不知道Key
使用哪种数据类型以及如何访问MyDictionary
中的Values
以满足新要求。
有什么想法吗?
自 24.02.2016 起修改:
我必须在原始问题中添加更多信息。
- 粒度仅在运行时已知
- 通过
array indexer []
对Values
的访问必须经过运行时优化。它将在很短的时间内被调用数百万次。
使用 MyDictionary
的类使用DateTime
对象来访问Values
。例:
public class ClientClass
{
public void AccessMyDictionary(DateTime date)
{
MyData data = MyDictionary[date.Year];
// Do something with data
}
}
在我看来,最明显的事情是将DateTime
作为索引器数据类型。然后在 MyDictionary
类中创建索引器来处理粒度。例:
public enum Period
{
Yearly,
Quarterly,
Monthly,
Weekly,
Daily
}
public class MyDictionary
{
private Period period;
private SortedList<DateTime, MyData> sortedList;
public MyDictionary(Period period)
{
this.period = period;
sortedList = new SortedList<DateTime, MyData>();
}
public MyData this[DateTime i]
{
get
{
// Implement here an algorithm for granularity, similar to the one of Tomas Lycken in the 1st answer
}
set
{
// Implement here an algorithm for granularity, similar to the one of Tomas Lycken in the 1st answer
}
}
}
你觉得怎么样?该运行时是否经过优化?
非常感谢康斯坦丁
我会为各种粒度定义一些新的值对象,所有这些对象都派生自一个共同的基类Period
。然后,您可以用作这些键。例如
public abstract class Period { }
public class Quarter : Period
{
public int Quarter { get; }
public int Year { get; }
public Quarter(int year, int quarter)
{
if (year < 1800 || year > DateTime.UtcNow.Year)
{
throw new ArgumentOutOfRangeException(nameof(year));
}
if (quarter < 1 || quarter > 4)
{
throw new ArgumentOutOfRangeException(nameof(quarter));
}
Year = year;
Quarter = quarter;
}
}
当然,你会为Year
(只有一个属性)、Month
(有一个年和一个月,月份必须在 1 到 12 之间)、Week
(验证变得有点棘手,因为并非所有年份都有相同的周数)、Day
(不要忘记允许闰年!)定义类似的类型。
然后,您还可以为这些类型定义相等和哈希,以便如果它们的属性相等,则它们相等。(这是关于该主题的好读物!对于Quarter
,我会做类似的事情
public class Quarter
{
// properties and constructor ommitted
public override bool Equals(object other)
{
if (!(other is Quarter))
{
return false;
}
var quarter = (Quarter)other;
return quarter.Year == Year && quarter.Quarter == quarter;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
// The two hard-coded digits below should be primes,
// uniquely chosen per type (so no two types you define
// use the same primes).
int hash = (int) 2166136261;
// Suitable nullity checks etc, of course :)
hash = hash * 16777619 ^ Quarter.GetHashCode();
hash = hash * 16777619 ^ Year.GetHashCode();
return hash;
}
}
}
根据您将如何使用它们,您可能还希望覆盖==
和/或!=
。
现在,这些类型完全可以用作字典中的键,因此您可以这样做
var quarterlyReport = new SortedList<Quarter, Data>();
如果要避免手动定义Equals
和GetHashCode
,.NET 中的大多数关联集合都有一个构造函数,该构造函数采用键类型的相等比较器,用于处理此问题。 SortedList<TKey, TValue>
也有一个,所以与其覆盖上面的Equals
和GetHashCode
,不如为每个时期创建一个吊坠类型,例如
public class QuarterComparer : IComparer<Quarter>
{
int IComparer<Quarter>.Compare(Quarter p, Quarter q)
{
return p.Year < q.Year
? -1
: p.Year == q.Year
? p.Quarter < q.Quarter
? -1
: p.Quarter == q.Quarter
? 0
: 1
: 1;
}
public int Compare(Quarter p, Quarter q)
{
return (this as IComparer<Quarter>).Compare(p, q);
}
}
并将其传递给排序列表的构造函数:
var quarterlyData = new SortedList<Quarter, MyData>(new QuarterComparer());