Java 将元素添加到有限大小列表

Java 将元素添加到有限大小列表,java,collections,Java,Collections,我使用以下方法将元素添加到大小受限的ArrayList。如果ArrayList的大小超过,则删除以前的元素(如FIFO=“先进先出”)(version 1): //将“项”添加到“列表”中并满足列表的“限制” 公共静态无效添加(列表、最终T项、整数限制){ var size=list.size()+1; 如果(大小>限制){ var exeeded=大小-限制; 对于(变量i=0;i限制){ var exeeded=大小-限制; 子列表(0,exeed).clear(); } 列表。添加(项目)

我使用以下方法将元素添加到大小受限的
ArrayList
。如果
ArrayList
的大小超过,则删除以前的元素(如FIFO=“先进先出”)(version 1):

//将“项”添加到“列表”中并满足列表的“限制”
公共静态无效添加(列表、最终T项、整数限制){
var size=list.size()+1;
如果(大小>限制){
var exeeded=大小-限制;
对于(变量i=0;i
“版本1”方法有效。但是,我想通过使用子列表(Version2)来改进此方法:

publicstaticvoidadd(列表、最终T项、整数限制){
var size=list.size()+1;
如果(大小>限制){
var exeeded=大小-限制;
子列表(0,exeed).clear();
}
列表。添加(项目);
}
两种方法都有效。然而,我想知道“版本2”是否比“版本1”更具性能

编辑: 改进的“版本3”:

public static <T> void add(List<T> list, final T item, int limit) {
        var size = list.size() + 1;
        if (size > limit) {
            var exeeded = size - limit;
            if (exeeded > 1) {
                list.subList(0, exeeded).clear();
            } else {
                list.remove(0);
            }
        }
        list.add(item);
    }
publicstaticvoidadd(列表、最终T项、整数限制){
var size=list.size()+1;
如果(大小>限制){
var exeeded=大小-限制;
如果(超出>1){
子列表(0,exeed).clear();
}否则{
列表。删除(0);
}
}
列表。添加(项目);
}

问题1:问题是这一行:

list = list.subList(exeeded, list.size());
您正在重新分配变量
列表
,该变量不会更改为作为参数传递的对象,而只会更改为其本地对应项

问题2:子列表(在数组列表上)仍需要在某个时候重新创建数组。如果您不想这样做,可以使用
链接列表
。但一般来说,
ArrayList
总体上仍会表现得更好。由于基础阵列仅在超过最大容量时才需要重新创建,因此它通常无关紧要。
您还可以尝试实际移动数组,将每个元素移动到数组中的下一个插槽。这样,在添加新元素时,您就必须移动所有元素,但不需要重新创建数组。因此,您避免了对性能影响最大的堆之旅。

您似乎记住了
ArrayList
实现,其中
remove(0)
会增加复制备份数组中所有剩余元素的成本,如果您反复调用
remove(0)

在这种情况下,使用
子列表(0,number).clear()
是一个显著的改进,因为您只需支付复制元素一次的成本,而不是
次数

由于当
number
为1时,
remove(0)
subList(0,number).clear()
的复制成本是相同的,因此第三种变体可以节省在这种情况下为子列表创建临时对象的成本。然而,这是一个微小的影响,它不依赖于列表的大小(或输入的任何其他方面),通常不值得更复杂的代码。有关单个临时对象的成本讨论,请参见。甚至可以通过JVM的运行时优化器消除子列表构建的成本。因此,只有当您遇到实际的性能问题时,才应该使用这种条件,探查器将问题追溯到这一点,并且基准测试证明更复杂的代码具有积极的效果


但当您使用
ArrayDeque
时,这一切都是毫无意义的。此类在删除其头元素时没有复制成本,因此您可以简单地删除循环中的多余元素。

但是您可以更改参数
limit
,并且
excelled
-部分的目标是删除所有超出的项(可能是1或更高)。如果你有更好的版本,请分享作为答案。我需要对这个用例最有效率的解决方案。如果你不考虑替代方法(可能在现有的答案中是优越的和解释的),那么第二个版本是两个更好的方法。在许多列表实现中,没有区别,但有一些(例如
ArrayList
)如果他们被告知要一次删除整个列表,而不是多次调用remove,则可以避免大量不必要的工作。@nimo23:这听起来像是一种微优化,使代码变得不那么清晰,并且只有有限的好处。在我考虑把它作为一个潜在的性能改进之前,我必须确信这是一个实际的瓶颈。这就是说:如果子列表只是一个元素,那么理论上创建视图对象的成本可能会比简单的remove()调用成本高。因为您使用的是
列表
接口,并且没有指定实际的实现类,所以无法对性能进行说明。显然,您正在考虑
ArrayList
。然后,简单的答案是,不要使用这些变体,而是使用
ArrayDeque
。1。“使用
ArrayDeque
,我无法动态更改限制”是什么意思?你当然可以。与使用
ArrayList的方法相同。2.为什么你突然问是否“有什么不对劲”?您提出了一个性能问题,而
ArrayQue
没有您试图解决的性能问题。您可以从标头中删除,而不会复制基础数组中的任何元素。
public static <T> void add(List<T> list, final T item, int limit) {
        var size = list.size() + 1;
        if (size > limit) {
            var exeeded = size - limit;
            if (exeeded > 1) {
                list.subList(0, exeeded).clear();
            } else {
                list.remove(0);
            }
        }
        list.add(item);
    }
list = list.subList(exeeded, list.size());