¿Cómo eliminar todos los elementos nulos de una ArrayList o String Array?


188

Lo intento con un bucle como ese

// ArrayList tourists

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

Pero no es lindo. ¿Alguien puede sugerirme una mejor solución?


Algunos puntos de referencia útiles para tomar una mejor decisión:

Bucle While, bucle For y prueba de rendimiento del iterador


Respuestas:


365

Tratar:

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

Lea la API de Java . El código se lanzará java.lang.UnsupportedOperationExceptionpara listas inmutables (como las creadas con Arrays.asList); Vea esta respuesta para más detalles.


9
La complejidad temporal de List.removeAll()es n ^ 2 . Solo digo.
Hemanth

8
Para Java 8 o posterior, consulte la respuesta de @ MarcG a continuación.
Andy Thomas

2
@Hemanth ¿Puedes explicar cómo obtuviste esa complejidad de tiempo? Porque me parece bastante O(n)para ambos ArrayListy LinkedList.
Helder Pereira

1
@HelderPereira No creo que deba ser para este caso , ya que la fuente (línea 349) parece recorrer ambas listas ( contains()recorre toda la matriz) y dado que singletones solo un elemento, sería N * 1 = N. Sin embargo, en general lo sería N^2.
Moira

66
@Hemanth No, no lo es. Es n * m donde m es el número de elementos en este caso un singleton de nulo que es 1. Es O (n). Puede ver el código fuente aquí y ver si lee y escribe sobre la lista una vez, moviendo los elementos para dar cuenta del eliminado.
Tatarizar

117

A partir de 2015, esta es la mejor manera (Java 8):

tourists.removeIf(Objects::isNull);

Nota: Este código arrojará java.lang.UnsupportedOperationExceptionpara listas de tamaño fijo (como las creadas con Arrays.asList), incluidas las listas inmutables.


1
"Mejor" de qué manera? ¿Es más rápido que otros enfoques? ¿O es simplemente más legible en virtud de la brevedad?
Andy Thomas

15
No solo por brevedad, sino porque es más expresivo. Casi puede leerlo: "De los turistas, elimine si el objeto es nulo". Además, la forma antigua es crear una nueva colección con un único objeto nulo y luego solicitar eliminar el contenido de una colección de la otra. Parece un poco hack, ¿no te parece? Con respecto a la velocidad, tiene un punto, si la lista es realmente grande y el rendimiento es una preocupación, sugeriría probar en ambos sentidos. Supongo que sería removeIfmás rápido, pero es una suposición.
MarcG

1
Arrays.asListNo es inmutable . Es de tamaño fijo.
turbanoff

@turbanoff sí, tienes razón, por supuesto. Es de tamaño fijo solamente, actualizaré la respuesta.
MarcG

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

Será Lanza UnsupportedException si lo usa en Arrays.asList, ya que le dan Inmutable copia por lo que no se puede modificar. Ver abajo el código. Crea una copia mutable y no arrojará ninguna excepción.

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

18

No eficiente, pero corto

while(tourists.remove(null));

1
Desafortunadamente, su solución fue la única que funcionó para mí ... ¡gracias!
Pkmmte

simple y rápido

55
@mimrahe lo contrario de rápido, en realidad. terrible lento si tienes una gran lista.
Gewure

18

Si prefiere objetos de datos inmutables, o si simplemente no desea ser destructivo para la lista de entrada, puede usar los predicados de Guava.

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))

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

Esto puede ser más útil cuando tiene que eliminar elementos mientras recorre. La coincidencia es que estaba anulando los elementos que tratando de usar removeAll(..null..). ¡Gracias!
Mustafa

Puede que sea mejor establecer los valores como nulos y luego eliminarlos al final. El lote Eliminar en removeAll atraviesa la lista, con una ubicación de lectura y escritura e itera la lista una vez, moviendo la lectura pero no la escritura cuando llega a un valor nulo. .remove () podría ser legítimo tener que copiar la matriz completa cada vez que se llama.
Tatarizar

4

Pre-Java 8 debe usar:

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

Uso posterior a Java 8:

tourists.removeIf(Objects::isNull);

La razón aquí es la complejidad del tiempo. El problema con las matrices es que una operación de eliminación puede tardar O (n) en completarse. Realmente en Java, esta es una copia de matriz de los elementos restantes que se mueven para reemplazar el espacio vacío. Muchas otras soluciones que se ofrecen aquí desencadenarán este problema. El primero es técnicamente O (n * m) donde m es 1 porque es un singleton nulo: entonces O (n)

Debe eliminar All of the singleton, internamente hace un batchRemove () que tiene una posición de lectura y una posición de escritura. E itera la lista. Cuando llega a un valor nulo, simplemente itera la posición de lectura en 1. Cuando son lo mismo que pasa, cuando son diferentes, se sigue moviendo copiando los valores. Luego, al final, se ajusta al tamaño.

Efectivamente hace esto internamente:

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

Lo que puede ver explícitamente es una operación O (n).

Lo único que podría ser más rápido es si itera la lista desde ambos extremos, y cuando encuentra un valor nulo, establece su valor igual al valor que encontró al final y disminuye ese valor. E iterado hasta que los dos valores coinciden. Arruinaría el orden, pero reduciría enormemente la cantidad de valores que establezca frente a los que dejó solo. Es un buen método para saber, pero no ayudará mucho aquí, ya que .set () es básicamente gratuito, pero esa forma de eliminar es una herramienta útil para su cinturón.


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

Si bien esto parece bastante razonable, el .remove () en el iterador llama internamente:

ArrayList.this.remove(lastRet);

Que es nuevamente la operación O (n) dentro de la eliminación. Hace un System.arraycopy () que nuevamente no es lo que quieres, si te importa la velocidad. Esto lo hace n ^ 2.

También hay:

while(tourists.remove(null));

Que es O (m * n ^ 2). Aquí no solo iteramos la lista. Reiteramos toda la lista, cada vez que hacemos coincidir el nulo. Luego hacemos n / 2 (promedio) operaciones para hacer System.arraycopy () para realizar la eliminación. Literalmente, podría ordenar toda la colección entre elementos con valores y elementos con valores nulos y recortar el final en menos tiempo. De hecho, eso es cierto para todos los rotos. Al menos en teoría, el system.arraycopy real no es en realidad una operación N en la práctica. En teoría, teoría y práctica son lo mismo; en la práctica no lo son.


3

Hay una manera fácil de eliminar todos los nullvalores de collection. Debe pasar una colección que contenga nulo como parámetro al removeAll()método

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

yourCollection.removeAll(s1);

Esto funcionó mejor para mí. También le permite agregar fácilmente más de una entrada en su "matriz de filtros" que se pasa al método removeAll de la colección original.

3

La Objectsclase tiene una con la nonNull Predicateque se puede usar filter.

Por ejemplo:

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

1
Bienvenido a Stack Overflow. Al responder preguntas, intente agregar una explicación de su código. Regrese y edite su respuesta para incluir más información.
Tyler

3

Usando Java 8, puedes hacer esto usando stream()yfilter()

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

o

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

Para más información: Java 8 - Streams


1
Esta solución está funcionando con copia inmutable, es decir, -> List <String> listOfString = Arrays.asList ("test1", null, "test"); ..... también ! Gracias
Anurag_BEHS

2

Esta es una manera fácil de eliminar los valores nulos predeterminados de la lista de arrays

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

de lo contrario, el valor de cadena "nulo" se eliminará de la lista de arrays

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

1

Jugué con esto y descubrí que trimToSize () parece funcionar. Estoy trabajando en la plataforma Android, por lo que podría ser diferente.


2
Según el javadoc, trimToSizeno modifica los contenidos de a ArrayList. Si esto es diferente en Android, probablemente sea un error.
fabian

1

Podemos usar el iterador para lo mismo para eliminar todos los valores nulos.

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

1

Utilicé la interfaz de flujo junto con la operación de flujo por cobrar y un método auxiliar para generar una nueva lista.

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

private <T> boolean isNotNull(final T item) {
    return  item != null;
}

2
tourists.stream().filter(s -> s != null).collect(Collectors.toList());
1ac0

1

Principalmente estoy usando esto:

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

Pero después de aprender Java 8, cambié a esto:

List.removeIf(Objects::isNull);

0

Usando Java 8 esto se puede realizar de varias maneras usando flujos, flujos paralelos y removeIfmétodo:

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]

El flujo paralelo hará uso de los procesadores disponibles y acelerará el proceso para listas de tamaño razonable. Siempre es recomendable realizar una evaluación comparativa antes de usar transmisiones.


0

Similar a la respuesta @Lithium pero no arroja un error "La lista no puede contener tipo nulo":

   list.removeAll(Collections.<T>singleton(null));

0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.