排序列表的动态键<键,值>

本文关键字:排序 动态 列表 | 更新日期: 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 起修改:

我必须在原始问题中添加更多信息。

  1. 粒度仅在运行时已知
  2. 通过array indexer []Values的访问必须经过运行时优化。它将在很短的时间内被调用数百万次。
  3. 使用
  4. 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>();

如果要避免手动定义EqualsGetHashCode,.NET 中的大多数关联集合都有一个构造函数,该构造函数采用键类型的相等比较器,用于处理此问题。 SortedList<TKey, TValue>也有一个,所以与其覆盖上面的EqualsGetHashCode,不如为每个时期创建一个吊坠类型,例如

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());