简体   繁体   中英

Why will I ever use list.clear()

Arraylist in java has a method call clear(). Why would I chose to use clear over resigning reference ?

list.clear()

vs

list = new ArrayList() ? 

Looks like list.clear() will be slower, which in the second case, GC would deal with cleanup and make our life easy ?

You would use list.clear() when some other piece of code has a reference to the same list.

For example, if you have a situation like this, a re-assignment is not going to work:

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>()
}

If your list is local and you have full control, then creating a new one is certainly not a bad idea - in many situations creating a new list will be faster (although not always). See for example this related answer .

However you may be in a situation where your list has been shared across several objects. You then need to work on that shared reference.

Looks like list.clear() will be slower,

Not always.

clear() has to clear out the references you used, however when you create a new object, it has to clear out the memory for the object before you can use it.

which in the second case, GC would deal with cleanup and make our life easy ?

The GC is not free. When you fill your CPU caches with garbage they won't work so well. You can speed up code significantly by reusing objects. It depends on your use case as to which is faster.


It is hard to find a decent micro-benchmark to demonstrate this, but in real programs, where the code is more complex, the impact is much higher than you might expect.

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;
    }
}

prints

Reuse time: 1,964, New ArrayList time: 1,866
Reuse time: 1,889, New ArrayList time: 1,770
Reuse time: 1,163, New ArrayList time: 1,416
Reuse time: 1,250, New ArrayList time: 1,357
Reuse time: 1,253, New ArrayList time: 1,393
Reuse time: 1,106, New ArrayList time: 1,203
Reuse time: 1,103, New ArrayList time: 1,207
Reuse time: 1,113, New ArrayList time: 1,315
Reuse time: 1,104, New ArrayList time: 1,215
Reuse time: 1,106, New ArrayList time: 1,335

Note: the size of the buffer copied makes a difference.


The picture is far worse if you consider latency. This prints out the worst latency for each run.

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;
    }
}

prints when run with -Xmx32m

Reuse time: 74,879, New ArrayList time: 2,441,586
Reuse time: 26,889, New ArrayList time: 2,203,096
Reuse time: 25,920, New ArrayList time: 1,514,465
Reuse time: 13,013, New ArrayList time: 1,342,395
Reuse time: 12,368, New ArrayList time: 1,708,658
Reuse time: 12,272, New ArrayList time: 1,258,990
Reuse time: 12,559, New ArrayList time: 1,433,898
Reuse time: 12,144, New ArrayList time: 1,259,413
Reuse time: 12,433, New ArrayList time: 1,221,945
Reuse time: 12,352, New ArrayList time: 1,318,024

I guess there are several use cases, here is one from the top of my head:

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

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

...

fooList.clear();
...

1) There are cases, when it is not interchangeable. For instance, you got a list as a parameter. If you want the empty list be visible outside the method, the only solution is list.clear(). If you use new ArrayList(), the change will be local and the list outside the method will not be changed.

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

void main(...){
    ...
    list.add(...);
    doSmth(list);
    // Here we get the empty list
    System.out.println(list);
}

In such case you have no choice. Only list.clear() will work.

2) new ArrayList() requires additional memory. Where as list.clear() does not. list.clear() is preferred.

3) list.clear() is faster. This has nothing to do with GC. The elements contained in the list will be GC-ed anyway. But in the case of list.clear() merely internal counter is set to 0. Where as for new ArrayList() a new object will be allocated, constructor will be called and the new object be initialized. Thus new ArrayList() is slower. But in the reality the difference can be very small, compared to performance of other pieces in the application.

You're implementing a queue of some sort... and have several worker threads. Each was passed a reference to the queue when it was created.

LinkedList q = new LinkedList();
Worker worker1 = new Worker(q);
Worker worker2 = new Worker(q);

And yes, I realizes there are all sorts of issues with synchronization around that statement... you probably really want to be using something like a LinkedBlockingDeque instead...

So, you're merrily inserting things into this queue and the workers are doing q.poll() ... and for some reason, you want to flush the queue.

At this point, you can't just do q = new LinkedList() because the workers have the old q and won't see you create the new one. For that matter, if there's still stuff in there that you want to dump (or not process).

There are lots of ways of handling the problem... tell each worker here's the new q would be one. But another is just to call q.clear() and you've flushed the list that everyone is working out of.

And so, there is a use case for list.clear() . I'll admit its not the best one, people can probably think of better examples, but having the functionality there means that people can do it and can use this rather than going through other contortions to get the implementation to work like they want it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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