2014-07-01 1 views
3

Arraylist in java a un appel de méthode clear(). Pourquoi aurais-je choisi d'utiliser une référence claire plutôt qu'une démission?Pourquoi utiliserai-je jamais list.clear()

list.clear() 

vs

list = new ArrayList() ? 

On dirait list.clear() sera plus lente, qui, dans le second cas, GC traiterait de nettoyage et de rendre notre vie facile?

+0

Parce que java est passé par la valeur. –

+6

@SotiriosDelimanolis: Façon d'être obtus. Je pense que si vous creusez un peu plus loin, vous trouverez que Java [* passe les références par valeur. *] (Http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass -de-valeur) –

+1

'clear()' fera tout son élément GCAblae, où l'affecter à l'instance 'new' coûtera la création d'un nouvel objet et fera aussi tous les éléments de l'instance réelle de GCable + list GCable –

Répondre

4

Si votre liste est locale et que vous avez un contrôle total, alors en créer une nouvelle n'est certainement pas une mauvaise idée - dans de nombreuses situations, créer une nouvelle liste sera plus rapide (mais pas toujours). Voir par exemple this related answer.

Toutefois, vous pouvez être dans une situation où votre liste a été partagée entre plusieurs objets. Vous devez ensuite travailler sur cette référence partagée.

1

On dirait list.clear() sera plus lent,

Pas toujours. Clear() doit effacer les références que vous avez utilisées, mais lorsque vous créez un nouvel objet, il doit effacer la mémoire de l'objet avant de pouvoir l'utiliser.

qui dans le second cas, GC s'occuperait du nettoyage et nous faciliterait la vie?

Le CPG n'est pas libre. Lorsque vous remplissez vos caches CPU avec des déchets, ils ne fonctionneront pas aussi bien. Vous pouvez accélérer le code de manière significative en réutilisant des objets. Cela dépend de votre cas d'utilisation quant à savoir lequel est le plus rapide.


Il est difficile de trouver un micro-benchmark décent pour démontrer, mais dans les programmes réels, où le code est plus complexe, l'impact est beaucoup plus élevé que vous pourriez vous attendre.

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

impressions

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: la taille de la mémoire tampon copié fait une différence.


L'image est bien pire si vous considérez la latence. Cela imprime la pire latence pour chaque exécution.

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

impression Exécuté avec les -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 
+0

La situation principale où 'clear' est plus rapide est si la liste est grande et est entièrement réutilisée. Cependant, même dans cette situation, créer une nouvelle liste avec la capacité définie au bon niveau donnera probablement une meilleure performance. Le GC sera essentiellement le même dans les deux cas (à l'exception du GC supplémentaire de la liste elle-même qui est probablement marginal). Sauf si vous faites cela toutes les 1 ms! – assylias

+0

@assylias Si vous accédez aux données dans votre cache L1, il est 10-20 fois plus rapide que d'accéder aux données dans votre cache L3. Dans l'isolement, il y a peu de différence, si votre application fait quoi que ce soit d'autre, cela sera considérablement plus lent si la faille que vous créez signifie que vous finissez par utiliser un cache plus lent. –

+0

Ne peut pas discuter avec ça! – assylias

0

Je suppose qu'il ya plusieurs cas d'utilisation, voici un du haut de ma tête:

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

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

... 

fooList.clear(); 
... 
9

QU'AVEC list.clear() lorsque certains autre pièce de code a une référence à la même liste.

Par exemple, si vous avez une telle situation, une nouvelle affectation ne va pas travailler:

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

1) Il y a des cas, quand il est pas interchangeable. Par exemple, vous avez une liste en paramètre. Si vous voulez que la liste vide soit visible en dehors de la méthode, la seule solution est list.clear(). Si vous utilisez new ArrayList(), la modification sera locale et la liste en dehors de la méthode ne sera pas modifiée.

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

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

Dans ce cas, vous n'avez pas le choix. Seul list.clear() fonctionnera.

2) new ArrayList() requiert de la mémoire supplémentaire. Où comme list.clear() ne fait pas. list.clear() est préféré.

3) list.clear() est plus rapide. Cela n'a rien à voir avec GC. Les éléments contenus dans la liste seront GC-ed de toute façon. Mais dans le cas de list.clear(), seul le compteur interne est mis à 0. Où, comme pour le nouvel ArrayList(), un nouvel objet sera alloué, le constructeur sera appelé et le nouvel objet sera initialisé. Ainsi, la nouvelle ArrayList() est plus lente. Mais dans la réalité, la différence peut être très faible, par rapport à la performance des autres pièces de l'application.

0

Vous implémentez une file d'attente ... et avez plusieurs threads de travail. Chacun a été passé une référence à la file d'attente quand il a été créé.

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

Et oui, je réalise qu'il ya toutes sortes de problèmes avec la synchronisation autour de cette déclaration ... vous voulez probablement vraiment être utiliser quelque chose comme un LinkedBlockingDeque à la place ...

Ainsi, vous Insérez gaiement des choses dans cette file d'attente et les travailleurs font q.poll() ... et pour une raison quelconque, vous voulez vider la file d'attente.

À ce stade, vous ne pouvez pas simplement faire q = new LinkedList() car les travailleurs ont l'ancien q et ne verront pas vous créer le nouveau. D'ailleurs, s'il y a encore des choses que vous voulez sauvegarder (ou ne pas traiter).

Il existe de nombreuses façons de gérer le problème ... dites à chaque travailleur que le nouveau q en ferait partie. Mais une autre est juste d'appeler q.clear() et vous avez vidé la liste que tout le monde travaille. Par conséquent, il existe un cas d'utilisation pour list.clear(). Je dois admettre que ce n'est pas le meilleur, les gens peuvent probablement penser à de meilleurs exemples, mais avoir la fonctionnalité signifie que les gens peuvent le faire et peuvent le faire plutôt que de passer par d'autres contorsions pour obtenir la mise en œuvre de travailler comme ils le veulent.

Questions connexes