Java 为什么我会使用list.clear()

Java 为什么我会使用list.clear(),java,collections,garbage-collection,Java,Collections,Garbage Collection,java中的Arraylist有一个方法调用clear()。为什么我会选择使用clear而不是辞职推荐人 list.clear() vs 看起来list.clear()会比较慢,在第二种情况下,GC会处理清理并使我们的生活变得轻松?如果您的列表是本地的,并且您拥有完全的控制权,那么创建一个新列表肯定不是一个坏主意-在许多情况下创建一个新列表会更快(尽管并不总是如此)。例如,见 但是,您的列表可能已在多个对象之间共享。然后,您需要处理该共享引用 看起来list.clear()会更慢 不总是这样

java中的Arraylist有一个方法调用clear()。为什么我会选择使用clear而不是辞职推荐人

list.clear()
vs


看起来
list.clear()
会比较慢,在第二种情况下,GC会处理清理并使我们的生活变得轻松?

如果您的列表是本地的,并且您拥有完全的控制权,那么创建一个新列表肯定不是一个坏主意-在许多情况下创建一个新列表会更快(尽管并不总是如此)。例如,见

但是,您的列表可能已在多个对象之间共享。然后,您需要处理该共享引用

看起来list.clear()会更慢

不总是这样

clear()必须清除您使用的引用,但是,当您创建新对象时,必须先清除对象的内存,然后才能使用它

在第二种情况下,GC会处理清理工作,让我们的生活变得轻松

GC不是免费的。当你用垃圾填充你的CPU缓存时,它们不会工作得很好。通过重用对象,可以显著加快代码的速度。这取决于您的用例,哪一个更快


很难找到一个像样的微基准来证明这一点,但在实际程序中,代码更为复杂,其影响比您预期的要大得多

public class Main {

    public static void main(String... args) {
        for (int i = 0; i < 10; i++) {
            long t1 = timeReuse();
            long t2 = timeNewObject();
            System.out.printf("Reuse time: %,d, New ArrayList time: %,d%n", t1, t2);
        }
    }

    static final int RUNS = 50000;
    static final byte[] a = new byte[8 * 1024];
    static final byte[] b = new byte[a.length];

    private static long timeReuse() {
        long start = System.nanoTime();
        List<Integer> ints = new ArrayList<Integer>();
        for (int i = 0; i < RUNS; i++) {
            ints.clear();
            for (int j = -128; j < 128; j++)
                ints.add(j);

            System.arraycopy(a, 0, b, 0, a.length);
        }
        long time = System.nanoTime() - start;
        return time / RUNS;
    }

    private static long timeNewObject() {
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            List<Integer> ints = new ArrayList<Integer>(256);

            for (int j = -128; j < 128; j++)
                ints.add(j);
            System.arraycopy(a, 0, b, 0, a.length);
        }
        long time = System.nanoTime() - start;
        return time / RUNS;
    }
}
注意:复制的缓冲区大小会有所不同


如果你考虑延迟,情况会更糟。这将打印出每次运行的最差延迟

public class Main {

    public static void main(String... args) {
        for (int i = 0; i < 10; i++) {
            long t1 = timeReuse();
            long t2 = timeNewObject();
            System.out.printf("Reuse time: %,d, New ArrayList time: %,d%n", t1, t2);
        }
    }

    static final int RUNS = 50000;
    static final byte[] a = new byte[8 * 1024];
    static final byte[] b = new byte[a.length];

    private static long timeReuse() {
        List<Integer> ints = new ArrayList<Integer>();
        long longest = 0;
        for (int i = 0; i < RUNS; i++) {
            long start = System.nanoTime();
            ints.clear();
            for (int j = -128; j < 128; j++)
                ints.add(j);

            System.arraycopy(a, 0, b, 0, a.length);
            long time = System.nanoTime() - start;
            longest = Math.max(time, longest);
        }
        return longest;
    }

    private static long timeNewObject() {
        long longest = 0;
        for (int i = 0; i < RUNS; i++) {
            long start = System.nanoTime();
            List<Integer> ints = new ArrayList<Integer>(256);
            for (int j = -128; j < 128; j++)
                ints.add(j);
            System.arraycopy(a, 0, b, 0, a.length);
            long time = System.nanoTime() - start;
            longest = Math.max(time, longest);
        }
        return longest;
    }
}

我想有几个用例,下面是我脑海中的一个:

private List<Foo> fooList = new ArrayList<>();
private List<Foo> unmodifiableFooList = Collections.unmodifiableList(fooList);

public List<Foo> getFooList(){
  return unmodifiableFooList;
}

...

fooList.clear();
...
private-List-doulist=new-ArrayList();
私有列表不可修改的傻瓜列表=集合。不可修改列表(傻瓜列表);
公共列表get傻瓜列表(){
返回不可修改的傻瓜;
}
...
愚蠢的人;
...

当其他代码段引用同一列表时,您将使用
list.clear()

例如,如果您遇到这样的情况,重新分配将不起作用:

class Watcher {
    private final List<String> list;
    public Watcher(List<String> l) { list = l; }
    public void doSomething() {
        // Use list
    }
}

void main() {
    List<String> lst = new ArrayList<String>();
    Watcher w = new Watcher(lst);
    ...
    // At this point lst.clear() is different from lst = new List<String>()
}
类监视程序{
私人最终名单;
公共观察者(列表l){List=l;}
公共无效剂量测定法(){
//使用列表
}
}
void main(){
List lst=new ArrayList();
观察者w=新观察者(lst);
...
//此时,lst.clear()与lst=new List()不同
}
1)在某些情况下,它是不可互换的。例如,您得到了一个列表作为参数。如果希望空列表在方法外部可见,唯一的解决方案是list.clear()。如果使用new ArrayList(),则更改将是本地的,并且不会更改方法外部的列表

static void doSmth(List list){
    list.clear();
}

void main(...){
    ...
    list.add(...);
    doSmth(list);
    // Here we get the empty list
    System.out.println(list);
}
在这种情况下,你别无选择。只有list.clear()可以工作

2) 新的ArrayList()需要额外的内存。其中as list.clear()不适用。首选list.clear()

3) list.clear()更快。这与GC无关。列表中包含的元素无论如何都将被GC删除。但在list.clear()的情况下,仅内部计数器设置为0。其中,对于new ArrayList(),将分配一个新对象,调用构造函数并初始化新对象。因此,新的ArrayList()速度较慢。
但实际上,与应用程序中其他部分的性能相比,差异可能非常小。

您正在实现某种队列。。。并且有几个工作线程。创建队列时,每个队列都被传递一个引用

LinkedList q = new LinkedList();
Worker worker1 = new Worker(q);
Worker worker2 = new Worker(q);
是的,我意识到围绕这句话的同步有各种各样的问题。。。你可能真的想用LinkedBlockingDeque之类的东西来代替

因此,您正在愉快地将内容插入此队列,而工作人员正在执行
q.poll()
。。。出于某种原因,您希望刷新队列

此时,您不能只执行
q=newlinkedlist()
,因为工作人员拥有旧的
q
,不会看到您创建新的。对于这一点,如果仍然有东西在那里,你想转储(或不处理)

有很多方法可以解决这个问题。。。告诉这里的每个工人新的
q
将是一个。但另一种方法是调用
q.clear()
,您已经刷新了所有人都在处理的列表


因此,
list.clear()
有一个用例。我承认这不是最好的,人们可能会想出更好的例子,但拥有这些功能意味着人们可以做到这一点,并且可以使用这些功能,而不是通过其他的扭曲来让实现按他们想要的方式工作。

因为java是按值传递的。@SotiriosDelimanolis:太迟钝了。我想如果你再深入一点,你会发现Java
clear()
会让它的所有元素都变成GCAblae,而把它放到
new
实例中会花费创建新对象的成本,而且它也会让所有元素GCable+list的实际实例GCable。我认为所谓的重复询问了
clear()的时间方面
,而这一个询问逻辑,同时假设(非常正确地)
clear()
调用可能较慢。投票重新打开讨论。
clear
更快的主要情况是列表较大且完全重用。然而,即使在这种情况下,创建一个容量设置为正确级别的新列表可能会产生更好的性能。GC在这两种情况下本质上是相同的(除了列表本身的附加GC,它可能是边缘的)。除非你每1毫秒做一次@assylias如果访问一级缓存中的数据,则速度比访问三级缓存中的数据快10-20倍。孤立地说,没有什么区别,如果您的应用程序执行任何其他操作,那么如果您创建的垃圾意味着您最终使用的是较慢的缓存,那么这将大大降低速度。这一点无可争辩@假设您看到的是最差的延迟,那么创建新对象的延迟将增加100倍;)
class Watcher {
    private final List<String> list;
    public Watcher(List<String> l) { list = l; }
    public void doSomething() {
        // Use list
    }
}

void main() {
    List<String> lst = new ArrayList<String>();
    Watcher w = new Watcher(lst);
    ...
    // At this point lst.clear() is different from lst = new List<String>()
}
static void doSmth(List list){
    list.clear();
}

void main(...){
    ...
    list.add(...);
    doSmth(list);
    // Here we get the empty list
    System.out.println(list);
}
LinkedList q = new LinkedList();
Worker worker1 = new Worker(q);
Worker worker2 = new Worker(q);