Collections.emptyList () vs nouvelle instance

241

En pratique, vaut-il mieux retourner une liste vide comme celle-ci :

return Collections.emptyList();

Ou comme ça :

return new ArrayList<Foo>();

Ou cela dépend-il complètement de ce que vous allez faire avec la liste retournée?

mre
la source

Réponses:

300

La principale différence est qu'elle Collections.emptyList()renvoie une liste immuable , c'est-à-dire une liste à laquelle vous ne pouvez pas ajouter d'éléments. (Il en va de même pour l' List.of()introduction de Java 9.)

Dans les rares cas où vous ne souhaitez modifier la liste retournée, Collections.emptyList()et List.of()sont donc pas un bon choix.

Je dirais que renvoyer une liste immuable est parfaitement bien (et même la manière préférée) tant que le contrat (documentation) ne stipule pas explicitement différemment.


De plus, il se emptyList() peut que vous ne créiez pas un nouvel objet à chaque appel.

Les implémentations de cette méthode n'ont pas besoin de créer un objet List distinct pour chaque appel. L'utilisation de cette méthode est susceptible d'avoir un coût comparable à celui du champ portant le même nom. (Contrairement à cette méthode, le champ ne fournit pas de sécurité de type.)

L'implémentation de emptyListressemble à ceci:

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

Donc, si votre méthode (qui retourne une liste vide) est appelée très souvent, cette approche peut même vous donner des performances légèrement meilleures en termes de CPU et de mémoire.

aioobe
la source
4
Donc, serait Collections.emptyList()plus approprié pour, disons, la vérification des erreurs et autres?
mre
1
Les clients API ne recevront pas NullPointerExceptionen retournant à la Collections.emptyList()place de null.
realPK
@PK_J fait un point important. Collections.emptyList()est itérable et renvoie une longueur, il peut donc être utilisé dans des boucles for sans qu'une exception ne soit levée.
ndm13
qu'en est-il de l'utilisation List.of()?
4
@AJW, oui. Mais par rapport à, disons, new ArrayList<>()cela rend également la décision de conception claire; les éléments ne seront pas ajoutés à cette liste.
aioobe
51

À partir de Java 5.0, vous pouvez spécifier le type d'élément dans le conteneur:

Collections.<Foo>emptyList()

Je suis d'accord avec les autres réponses selon lesquelles pour les cas où vous souhaitez renvoyer une liste vide qui reste vide, vous devez utiliser cette approche.

Paul Jackson
la source
38
À partir de Java 7, vous pouvez laisser le compilateur déduire le paramètre type de l'appel de méthode générique à partir du type cible:List<Foo> list = Collections.emptyList()
Paul Jackson
28

Collections.emptyList est immuable donc il y a une différence entre les deux versions donc vous devez considérer les utilisateurs de la valeur retournée.

Le retour new ArrayList<Foo>crée toujours une nouvelle instance de l'objet, il a donc un coût supplémentaire très léger qui peut vous donner une raison d'utiliser Collections.emptyList. J'aime utiliser emptyListsimplement parce que c'est plus lisible.

Jeff Foster
la source
14

Soyez prudent cependant. Si vous revenez Collections.emptyList()et essayez ensuite de faire quelques changements avec lui comme add()ou smth comme ça, u aura un UnsupportedOperationException()car Collections.emptyList()retourne un objet immuable.

Sergey Frolov
la source
7

J'irais avec Collections.emptyList()si la liste retournée n'est en aucun cas modifiée (car la liste est immuable), sinon j'irais avec l'option 2.

L'avantage de Collections.emptyList()est que la même instance statique est renvoyée à chaque fois et qu'il n'y a donc pas de création d'instance pour chaque appel.

S73417H
la source
3

Utilisez Collections.emptyList () si vous voulez vous assurer que la liste renvoyée n'est jamais modifiée. C'est ce qui est retourné en appelant emptyList ():

/**
 * The empty list (immutable). 
 */
public static final List EMPTY_LIST = new EmptyList();
Atul
la source
Je suis arrivé ici en essayant de savoir si appeler Collections.emptyList()avait un coût de construction. Voir les détails de l'implémentation (mais probablement pas la même sur toutes les JVM) confirme que ce n'est pas le cas. @Atul, de quelle machine virtuelle Java est-ce?
wjl
2

Les réponses données soulignent le fait que emptyList()renvoie un immuable Listmais ne donnent pas d'alternatives. Les ArrayList(int initialCapacity)cas spéciaux du constructeur , 0donc le retour new ArrayList<>(0)au lieu de new ArrayList<>()pourrait également être une solution viable:

/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

[...]

/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

(sources de Java 1.8.0_72)

René
la source
Je suis en désaccord avec votre approche. Vous économisez un peu de mémoire et de CPU lors de l'initialisation, mais si la liste que vous avez renvoyée est jamais modifiée, vous perdez ce temps lorsque la liste réaffecte un nouveau tableau. Si de nombreux éléments sont ajoutés à la liste au fil du temps, cela pourrait se transformer en un goulot d'étranglement de performances en raison du taux de croissance beaucoup plus lent . Je préfère de loin m'en tenir à la convention d'une liste vide non modifiable ou d'une liste utilisable et modifiable.
Patrick M
1
Comme j'ai essayé de le souligner avec ma formulation (cela pourrait être viable ): tout dépend de votre cas d'utilisation. Je généralement soit retourner mutables ou Collections unmutable, pas un mélange selon wether ils sont vides ou non. Et pour contrer la « demande beaucoup plus lente »: c'est la mise en œuvre actuelle.
René
Oh mec, regarde-moi en citant les versions majeures de JDK 2 obsolètes. Donc java8 évite complètement le goulot d'étranglement en passant à la capacité par défaut à partir d'une taille initiale de 0. Désolé, j'avais tellement tort.
Patrick M