web-dev-qa-db-fra.com

Collections.emptyList () au lieu de null check?

Si j'ai une collection rarement utilisée dans une classe qui peut être instanciée plusieurs fois, je peux parfois avoir recours à "l'idiome" suivant afin de sauvegarder les créations d'objet inutiles:

List<Object> list = null;

void add(Object object) {
    if (list == null)
        list = new ArrayList<Object>();

    list.add(object);
}

// somewhere else
if (list != null)
    for (Object object : list)
         ;

Maintenant, je me demandais si je ne pouvais pas éliminer ces vérifications nulles en utilisant Collections.emptyList(). Cependant, je devrais alors modifier le if check in add() comme suit:

if (list == Collections.<Object>emptyList())
    list = new ArrayList<Object>();

Existe-t-il un meilleur moyen de gérer cela que d’allouer une nouvelle collection vide à chaque fois?

EDIT: juste pour être clair, je voudrais utiliser Collections.emptyList (), mais la vérification ci-dessus dans add () est vraiment vraiment moche ... Je me demandais s'il y avait une meilleure façon de le faire ou même une toute autre façon de gérer cela.

15
Philip Kamenarsky

pour sauvegarder des créations d'objet inutiles

C’est une très mauvaise idée, qui va encombrer votre code de vérifications == null et d’autres manipulations de cas dans les coins (et aboutira probablement à des exceptions null de pointeur de toute façon)!

Je me demandais maintenant si je ne pouvais pas éliminer ces contrôles nuls à l'aide de Collections.emptyList()

Non, pas vraiment. emptyList() renvoie une liste vide. Vous pourriez faites 

if (list.equals(Collections.<Object>emptyList()))

mais cela déclenchera toujours une exception NullPointerException si list == null, donc ce n’est toujours pas ce que vous recherchez.

Ma recommandation: initialisez toujours la liste sur new ArrayList<Object> ou, si vous souhaitez par exemple renvoyer une liste vide à partir d'une méthode, utilisez plutôt Collections.emptyList(). (Cela retourne la même instance à chaque fois, donc pas de création d'objet inutile là-bas non plus.)

Et utilisez ensuite .isEmpty() pour vérifier si une collection est vide ou non.

16
aioobe

Les réponses suggérées sont tout à fait correctes. Juste un petit conseil: dans Java 8, vous pouvez utiliser la nouvelle classe Facultatif pour gérer le cas où l'instance de liste est nulle, dans une approche plus fonctionnelle. 

Par exemple, quelque chose comme ceci:

public static List<String> addElement(List<String> list, String toAdd) {
       List<String> newList = Optional.ofNullable(list).orElse(new ArrayList<>());
       newList.add(toAdd);
       return newList;
}
9
Stas

Voici ce que j'utilise comme méthode d'assistance dans certains de mes codes. Cela fonctionne vraiment bien pour réduire le nombre de chèques nuls que je devrais normalement placer avant de parcourir des listes. Si vous voulez une liste qui ne soit pas immuable, vous pouvez retourner un nouvel objet de liste au lieu de Collections.emptyList

/**
 * Helper method to return an empty list if provided one is null.
 *
 * @param list the list
 * @return the provided list or an empty one if it was null
 */
private static <T> List<T> emptyIfNull(List<T> list) {
    if (list == null) {
        return Collections.emptyList();
    }
    return list;
}

Vous utilisez alors simplement la méthode d'assistance comme ceci:

for (Object object : emptyIfNull(existingList)) { ... }

Si l'objet de liste est null, la méthode d'assistance renvoie la liste vide statique et le contenu de votre boucle est ignoré. C’est un bon moyen d’éviter de créer des contrôles nuls qui encapsulent les itérations de la liste.

J'ai fait en sorte que les éléments internes de la liste soient de type Object uniquement pour l'exemple, mais vous voudriez évidemment que ce soit ce qui est le plus logique pour votre utilisation.

2
Aaron

emptyList () n'alloue pas un objet à chaque fois.

Je créerais moins de l'objet qui contient la liste afin que vous puissiez créer la liste à chaque fois.

Ce que tu peux faire c'est

private List<Object> list = Collections.emptyList();

private List<Object> listForWrite() {
    return list.isEmpty() ? list = new ArrayList<Object>() : list;
}


void add(Object object) {
    listForWrite().add(object);
}


// avoid creating an Iterator every time.
for (int i = 0, size = list.size(); i < size; i++) {
     ;
}
1
Peter Lawrey

Je trouve plus facile de suivre cette convention:

  1. Si le but de mes méthodes est de renvoyer une collection, la méthode ne renvoie jamais null. Null est ambigu. À la place, je retourne Collection.emptyXXX() ou ImmutableXXX.of() si vous utilisez Guava.

  2. Si j'ai un objet qui maintient une liste interne en tant que membre, instanciez-le dans le constructeur. J'essaie de ne pas faire d'instanciation paresseuse à moins de pouvoir prouver que c'est un gain significatif, car le code paresseux, à mon avis , a tendance à être plus difficile à déboguer lorsque des problèmes surviennent.

Je vois vraiment des collections vides immuables ou non modifiables faisant partie d'un contrat extérieur à des objets. Si vous utilisez la collection en interne, l'utilisation de collections immuables ne peut réellement être utilisée que si vous avez une bonne raison (concurrence, cohérence, immuabilité de l'objet).

0
Nick Campion

Voici une variante d'utilisation facultative de @Stas, mais également de la collection isEmpty immutable telle que demandée à l'origine dans la question:

public static List<String> addElement(List<String> list, String toAdd) {
   List<String> newList = Optional.ofNullable(list).orElse(Collections.emptyList());
   newList.add(toAdd);
   return newList;
}

Cette approche est aussi ce qui se rapproche le plus de la capacité de Nice en Javascript d'utiliser un tableau vide si la collection est nulle.

Par exemple:

// no need to indent everything inside a null check of myObjects
for (MyObj myObj : Optional.ofNullable(myObjects).orElse(Collections.emptyList())){
    // do stuff with myObj
}
0
Adam Wise

Si vous utilisez uniquement la liste pour les itérations, vous pouvez simplement utiliser: for (Object object : list) qui ne ferait rien pour les listes vides, c’est-à-dire pas une seule itération.

Sinon, il suffit de cocher list.isEmpty().

0
Thomas

Vous pouvez créer une classe utilitaire avec des méthodes statiques, comme:

public class ListUtil {

/**
 * Checks if {@link List} is null or empty.
 *
 * @param <E> the generic type
 * @param list the list
 * @return true, if is null or empty
 */
public static <E> boolean isNullOrEmpty(List<E> list) {
    return list == null || list.size() == 0;
}

/**
 * Checks if {@link List} is not null and empty.
 *
 * @param <E> the generic type
 * @param list the list
 * @return true, if is not null and empty
 */
public static <E> boolean isNotNullAndEmpty(List<E> list) {
    return list != null && list.size() != 0;
}

}

0
Tapas Bose