Java 为什么我会使用list.clear()
java中的Arraylist有一个方法调用clear()。为什么我会选择使用clear而不是辞职推荐人Java 为什么我会使用list.clear(),java,collections,garbage-collection,Java,Collections,Garbage Collection,java中的Arraylist有一个方法调用clear()。为什么我会选择使用clear而不是辞职推荐人 list.clear() vs 看起来list.clear()会比较慢,在第二种情况下,GC会处理清理并使我们的生活变得轻松?如果您的列表是本地的,并且您拥有完全的控制权,那么创建一个新列表肯定不是一个坏主意-在许多情况下创建一个新列表会更快(尽管并不总是如此)。例如,见 但是,您的列表可能已在多个对象之间共享。然后,您需要处理该共享引用 看起来list.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:太迟钝了。我想如果你再深入一点,你会发现Javaclear()
会让它的所有元素都变成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);