Comment supprimer tous les éléments nuls d'un ArrayList ou d'un String Array?

188

J'essaye avec une boucle comme ça

// ArrayList tourists

for (Tourist t : tourists) {
    if (t != null) {     
        t.setId(idForm); 
    }   
}

Mais ce n'est pas sympa. Quelqu'un peut-il me suggérer une meilleure solution?


Quelques repères utiles pour prendre une meilleure décision:

Tandis que la boucle, pour la boucle et le test de performance d'itérateur

Juan de Dios
la source
2
utiliser Iterator? Creusez java-doc. download.oracle.com/javase/6/docs/api/java/util/…
Nishant

Réponses:

365

Essayer:

tourists.removeAll(Collections.singleton(null));

Lisez l' API Java . Le code lancera java.lang.UnsupportedOperationExceptionpour les listes immuables (telles que créées avec Arrays.asList); voir cette réponse pour plus de détails.

Lithium
la source
9
La complexité temporelle de List.removeAll()est n ^ 2 . Juste dire.
Hemanth
8
Pour Java 8 ou version ultérieure, consultez la réponse de @ MarcG ci-dessous.
Andy Thomas
2
@Hemanth Pouvez-vous expliquer comment vous avez obtenu cette complexité temporelle? Parce que cela O(n)me semble tout à fait pour ArrayListet LinkedList.
Helder Pereira du
1
@HelderPereira Je ne pense pas que cela devrait être pour ce cas , car la source (ligne 349) semble faire une boucle sur les deux listes ( contains()boucle tout le tableau) et comme il singletonne s'agit que d'un élément N * 1 = N. Cependant, généralement, ce serait le cas N^2.
Moira
6
@Hemanth Non, ce n'est pas le cas. C'est n * m où m est le nombre d'éléments dans ce cas un singleton de nul qui vaut 1. C'est O (n). Vous pouvez voir le code source ici et voir qu'il lit et écrit une fois sur la liste, en déplaçant les éléments pour tenir compte de celui qui a été refacturé.
Tatarize
117

À partir de 2015, c'est le meilleur moyen (Java 8):

tourists.removeIf(Objects::isNull);

Remarque: Ce code sera lancé java.lang.UnsupportedOperationExceptionpour les listes de taille fixe (telles que créées avec Arrays.asList), y compris les listes immuables.

MarcG
la source
1
"Meilleur" de quelle manière? Est-ce plus rapide que les autres approches? Ou est-il simplement plus lisible en raison de sa brièveté?
Andy Thomas le
15
Non seulement à cause de la brièveté, mais parce que c'est plus expressif. Vous pouvez presque le lire: "Des touristes, supprimez si l'objet est nul". En outre, l'ancienne méthode consiste à créer une nouvelle collection avec un seul objet nul, puis à demander de supprimer le contenu d'une collection de l'autre. Cela semble un peu un hack, tu ne trouves pas? En ce qui concerne la vitesse, vous avez un point, si la liste est vraiment longue et que les performances sont un problème, je suggérerais de tester les deux méthodes. Je suppose que removeIfc'est plus rapide, mais c'est une supposition.
MarcG
1
Arrays.asListn'est pas immuable . Il est de taille fixe.
turbanoff
@turbanoff oui, vous avez raison, bien sûr. C'est de taille fixe uniquement, je vais mettre à jour la réponse.
MarcG
46
list.removeAll(Collections.singleton(null));

Il sera Lancers UnsupportedException si vous l' utilisez sur Arrays.asList car il vous donne Immuable copie ne peut donc pas être modifié. Voir ci-dessous le code. Il crée une copie Mutable et ne lèvera aucune exception.

public static String[] clean(final String[] v) {
    List<String> list = new ArrayList<String>(Arrays.asList(v));
    list.removeAll(Collections.singleton(null));
    return list.toArray(new String[list.size()]);
}
AZ_
la source
18

Pas efficace, mais court

while(tourists.remove(null));
Peter Lawrey
la source
1
Malheureusement, votre solution a été la seule qui a fonctionné pour moi ... merci!
Pkmmte
simple et rapide
5
@mimrahe est l'opposé de rapide, en fait. terrible lent si vous avez une grande liste.
Gewure
18

Si vous préférez des objets de données immuables, ou si vous ne voulez tout simplement pas être destructeur pour la liste d'entrée, vous pouvez utiliser les prédicats de Guava.

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))
James Kojo
la source
7
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }
Mat Mannion
la source
Cela peut être plus utile lorsque vous devez supprimer des éléments lors de la traversée. La coïncidence est que j'annulais les éléments plutôt que d'essayer de les utiliser removeAll(..null..). Merci!
Mustafa
Vous feriez peut-être mieux de définir les valeurs sur null, puis de les supprimer à la fin. Le batchRemove dans removeAll traverse la liste, avec un emplacement de lecture et d'écriture et itère la liste une fois, en déplaçant la lecture mais pas l'écriture quand elle atteint une valeur nulle. .remove () il se peut qu'il soit légitimement obligé de copier le tableau entier à chaque fois qu'il est appelé.
Tatarize
4

Avant Java 8, vous devez utiliser:

tourists.removeAll(Collections.singleton(null));

Utilisation post-Java 8:

tourists.removeIf(Objects::isNull);

La raison ici est la complexité du temps. Le problème avec les tableaux est qu'une opération de suppression peut prendre du temps O (n). En réalité, en Java, il s'agit d'une copie de tableau des éléments restants déplacés pour remplacer l'emplacement vide. De nombreuses autres solutions proposées ici déclencheront ce problème. Le premier est techniquement O (n * m) où m vaut 1 car c'est un singleton nul: donc O (n)

Vous devriez removeAll du singleton, en interne, il fait un batchRemove () qui a une position de lecture et une position d'écriture. Et répète la liste. Lorsqu'il atteint une valeur nulle, il itère simplement la position de lecture de 1. Quand ils sont identiques, il passe, quand ils sont différents, il continue à copier les valeurs. Puis à la fin, il s'adapte à la taille.

Il le fait efficacement en interne:

public static <E> void removeNulls(ArrayList<E> list) {
    int size = list.size();
    int read = 0;
    int write = 0;
    for (; read < size; read++) {
        E element = list.get(read);
        if (element == null) continue;
        if (read != write) list.set(write, element);
        write++;
    }
    if (write != size) {
        list.subList(write, size).clear();
    }
}

Ce que vous pouvez voir explicitement est une opération O (n).

La seule chose qui pourrait jamais être plus rapide est si vous avez itéré la liste des deux extrémités, et lorsque vous avez trouvé un null, vous définissez sa valeur égale à la valeur que vous avez trouvée à la fin et décrémentez cette valeur. Et itéré jusqu'à ce que les deux valeurs correspondent. Vous gâcheriez l'ordre, mais réduiriez considérablement le nombre de valeurs que vous définissez par rapport à celles que vous avez laissées seules. Ce qui est une bonne méthode à connaître mais qui n'aidera pas beaucoup ici car .set () est fondamentalement gratuit, mais cette forme de suppression est un outil utile pour votre ceinture.


for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }

Bien que cela semble assez raisonnable, le .remove () sur l'itérateur appelle en interne:

ArrayList.this.remove(lastRet);

Ce qui est encore l'opération O (n) dans l'enlèvement. Il fait un System.arraycopy () qui n'est pas non plus ce que vous voulez, si vous vous souciez de la vitesse. Cela en fait n ^ 2.

Il y a aussi:

while(tourists.remove(null));

Qui est O (m * n ^ 2). Ici, nous ne faisons pas qu'itérer la liste. Nous réitérons la liste entière, chaque fois que nous correspondons à la valeur nulle. Ensuite, nous effectuons n / 2 (moyennes) opérations pour faire le System.arraycopy () pour effectuer la suppression. Vous pouvez littéralement trier toute la collection entre les éléments avec des valeurs et les éléments avec des valeurs nulles et couper la fin en moins de temps. En fait, c'est vrai pour tous les cassés. Au moins en théorie, le system.arraycopy réel n'est pas réellement une opération N en pratique. En théorie, théorie et pratique sont la même chose; en pratique, ils ne le sont pas.

Tatarize
la source
3

Il existe un moyen simple de supprimer toutes les nullvaleurs de collection.Vous devez passer une collection contenant null comme paramètre à la removeAll()méthode

List s1=new ArrayList();
s1.add(null);

yourCollection.removeAll(s1);
shiv
la source
Cela a fonctionné le mieux pour moi. Il vous permet également d'ajouter facilement plus d'une entrée dans votre "tableau de filtres" qui est transmise à la méthode removeAll de la collection d'origine.
3

La Objectsclasse a un nonNull Predicatequi peut être utilisé avec filter.

Par exemple:

tourists.stream().filter(Objects::nonNull).collect(Collectors.toList());
JeffF
la source
1
Bienvenue dans Stack Overflow. Lorsque vous répondez aux questions, essayez d'ajouter une explication de votre code. Veuillez revenir en arrière et modifier votre réponse pour inclure plus d'informations.
Tyler
3

En utilisant Java 8, vous pouvez le faire en utilisant stream()etfilter()

tourists = tourists.stream().filter(t -> t != null).collect(Collectors.toList())

ou

tourists = tourists.stream().filter(Objects::nonNull).collect(Collectors.toList())

Pour plus d'informations: Java 8 - Streams

Jad Chahine
la source
1
Cette solution fonctionne avec la copie immuable ie -> List <String> listOfString = Arrays.asList ("test1", null, "test"); ..... aussi ! Merci
Anurag_BEHS
2

C'est un moyen facile de supprimer les valeurs nulles par défaut de l'arraylist

     tourists.removeAll(Arrays.asList(null));  

sinon, la valeur de chaîne "null" est supprimée de l'arraylist

       tourists.removeAll(Arrays.asList("null"));  
Jobin_vibes
la source
1

J'ai joué avec cela et j'ai découvert que trimToSize () semble fonctionner. Je travaille sur la plate-forme Android donc ça pourrait être différent.

le blitz
la source
2
Selon le javadoc, trimToSizene modifie pas le contenu d'un fichier ArrayList. Si c'est différent sous Android, c'est probablement un bug.
fabian
1

Nous pouvons utiliser l'itérateur pour le même pour supprimer toutes les valeurs nulles.

Iterator<Tourist> itr= tourists.iterator();
while(itr.hasNext()){
    if(itr.next() == null){
        itr.remove();
    }
}
amit
la source
1

J'ai utilisé l'interface de flux avec l'opération de collecte de flux et une méthode d'assistance pour générer une nouvelle liste.

tourists.stream().filter(this::isNotNull).collect(Collectors.toList());

private <T> boolean isNotNull(final T item) {
    return  item != null;
}
Mabi
la source
2
tourists.stream().filter(s -> s != null).collect(Collectors.toList());
1ac0
1

J'utilise principalement ceci:

list.removeAll(Collections.singleton(null));

Mais après avoir appris Java 8, je suis passé à ceci:

List.removeIf(Objects::isNull);
Maged
la source
0

En utilisant Java 8, cela peut être effectué de différentes manières en utilisant des flux, des flux parallèles et une removeIfméthode:

List<String> stringList = new ArrayList<>(Arrays.asList(null, "A", "B", null, "C", null));
List<String> listWithoutNulls1 = stringList.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
List<String> listWithoutNulls2 = stringList.parallelStream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
stringList.removeIf(Objects::isNull); //[A,B,C]

Le flux parallèle utilisera les processeurs disponibles et accélérera le processus pour des listes de taille raisonnable. Il est toujours conseillé d'effectuer un benchmark avant d'utiliser les flux.

akhil_mittal
la source
0

Similaire à la réponse @Lithium, mais ne renvoie pas d'erreur "La liste peut ne pas contenir de type null":

   list.removeAll(Collections.<T>singleton(null));
HannahCarney
la source
0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
cunhaf
la source