繁体   English   中英

从排序列表中删除最旧的插入数据

[英]Removing oldest inserted data from sorted list

我有一个对象列表,随着越来越多的数据被添加到其中,它会随着时间的推移而扩展,但在某个时候会有固定的最大大小。

public class Calculation 
{
  public List<DataUnit> DataUnits { get; set; } = new();

  public void AddSorted(DataUnit unit)
  {
    int index = DataUnits.BinarySearch(unit);
    DataUnits.Insert((index>=0) x : ~x, unit);
  }

  public void AddData(DataUnit unit)
  {
    AddSorted(unit);
    if(DataUnits.Count > 30)
    {
      // i need some sort of solution here
      DataUnits.RemoveOldest();
    }
  }

  public void SomeCalculation()
  {
    // performs some calculation, that is O(1) with a sorted list and O(N) with a non sorted list
  }
    
}

问题是对性能、RAM 和时间的限制。 实际代码(不是上面的虚拟代码)将在资源有限的机器上在 1 秒的时间范围内输入大量数据(大约 1000-2000 个数据单元)。 我们必须对这个列表进行计算,这比对排序列表进行计算要快得多。 而且,由于每秒都会有新数据出现,因此所有计算都必须在一秒钟内完成。

实现这个的最佳方法是什么?

我考虑过在每次计算之前分别对列表进行排序,但我担心 O(sqrt(n)) 不会削减它。 我的第二个想法是第二个列表,它只保留插入排序的时间,但是有很多数据,我认为我们在持有这些数据单元的两个列表时可能会达到资源限制。

// 编辑

有关数据单元的附加信息

public class DataUnit : IComparable
{
  public IComparable Data { get; set; }
  public int CompareTo(object? o)
  {
    if(o is DataUnit other)
    {
      return Data.CompareTo(other.Data);
    }
    return 0;
  }
}

有关计算的属性SomeCalculation方法对所有当前数据执行密集计算。 此计算仅适用于按其属性Data排序的列表(一些索引数学魔法,此处不重要)。

所以最后,List DataUnits必须按Data排序。 由于我们对 SomeCalculation 的调用比对SomeCalculation的调用要多得多, AddData我的实现使用带有排序插入的列表,而不是每次调用SomeCalculation时都对列表进行排序。

问题: DataUnits列表达到固定大小(例如 30 个元素),之后将保持其大小。 如果一个元素被添加到列表中,最旧的 DataUnit object 应该被删除(因此不包括在计算中)

我不确定我是否理解正确,但根据 MSDN List.Add 将元素附加到列表的末尾。 简单地添加元素(总是 go 到最后),计算它们的数量并在达到阈值后删除第一个不是最简单的吗? (又名最旧的?)当然,这期望您不会更改列表中元素的顺序。 类似于循环缓冲区。

像这样:

public void AddData(DataUnit unit)
{
   DataUnits.Add(unit);
   if (DataUnits.Count() > 30)
   {
      DataUnits.RemoveAt(0);
   }
}

编辑:Mathew Watson 的评论让我思考了一下。 我相信这样你可以对数组进行排序并删除最后一个元素:

  public class Calculation 
  {
    private DataUnit lastElement = null;

    public List<DataUnit> DataUnits { get; set; } = new();

    public void AddSorted(DataUnit unit)
    {
      int index = DataUnits.BinarySearch(unit);
      DataUnits.Insert((index>=0) x : ~x, unit);
    }

    public void AddData(DataUnit unit)
    {
      AddSorted(unit);

      // you don't need to use LINQ, you may test it against Count
      if (DataUnits.Any() == false)
      {
        this.lastElement = unit;
      }

      if(DataUnits.Count > 30)
      {
        if (this.lastElement != null)
        {
          DataUnits.Remove(this.lastElement);
          this.lastElement = unit;
        }
      }
    }

因为您只需要知道在某个元素和第 30 个元素之间有 30 次添加(没有删除)。 所以你需要做的就是记住当前元素,计算 30 个添加项,然后删除该元素,你添加的元素将成为新的删除候选元素。

如果SomeCalculation的性能确实是最重要的,并且AddData中的额外开销是可以接受的,那么您的方法就可以了。 始终保持列表排序,不要在每次执行SomeCalculation时都对其进行排序。

移除最旧的元素:保留一个固定大小的DataUnit队列(这些是引用),除了将新元素插入到排序列表中外,将其添加到队列的末尾。 如果队列大小超过了预期的大小,则从队列的前面移除一个元素,然后在排序列表中搜索它并将其从列表中移除。

搜索要删除的元素可以使用二分查找来完成,但是从排序列表中删除它是O(n) ,但是在列表中间插入一个新元素也是如此,所以性能是相似的。

您实际上可以删除旧元素并在排序列表的一次传递中插入新元素,以略微提高性能,但我不会 go 这样做,因为小列表的收益将是微不足道的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM