[英]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.