Como remover todos os elementos nulos de um ArrayList ou String Array?

Respostas:

365

Experimentar:

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

Leia a API Java . O código será lançado java.lang.UnsupportedOperationExceptionpara listas imutáveis ​​(como criadas com Arrays.asList); veja esta resposta para mais detalhes.

Lítio
fonte
9
A complexidade temporal de List.removeAll()é n ^ 2 . Apenas dizendo.
Hemanth 03/02
8
Para Java 8 ou posterior, consulte a resposta de @ MarcG abaixo.
Andy Thomas
2
@ Hemanth Você pode explicar como conseguiu essa complexidade de tempo? Porque me parece bastante O(n)para ambos ArrayListe LinkedList.
Helder Pereira
1
@HelderPereira Eu não acho que deveria ser para esse caso , já que a fonte (linha 349) parece percorrer as duas listas (faz um contains()loop em toda a matriz) e já que o singletoné apenas um elemento N * 1 = N. No entanto, geralmente seria N^2.
Moira
6
@ Hemanth Não, não é. É n * m onde m é o número de elementos, neste caso, um singleton nulo que é 1. É O (n). Você pode ver o código-fonte aqui e vê-lo ler e escrever sobre a lista uma vez, movendo os elementos para dar conta do remvoed.
Tatarize 28/05
116

A partir de 2015, esta é a melhor maneira (Java 8):

tourists.removeIf(Objects::isNull);

Nota: Este código será lançado java.lang.UnsupportedOperationExceptionpara listas de tamanho fixo (como criadas com Arrays.asList), incluindo listas imutáveis.

MarcG
fonte
1
"Melhor" de que maneira? É mais rápido que outras abordagens? Ou é apenas mais legível em virtude da brevidade?
Andy Thomas
15
Não só por causa da brevidade, mas porque é mais expressivo. Você quase pode lê-lo: "Dos turistas, remova se o objeto for nulo". Além disso, a maneira antiga é criar uma nova coleção com um único objeto nulo e pedir para remover o conteúdo de uma coleção da outra. Parece um pouco de hack, você não acha? Em relação à velocidade, você tem razão: se a lista é realmente grande e o desempenho é uma preocupação, sugiro testar os dois lados. Meu palpite seria que removeIfé mais rápido, mas é um palpite.
MarcG
1
Arrays.asListnão é imutável . É de tamanho fixo.
turbanoff
@turbanoff sim, você está certo, é claro. É apenas de tamanho fixo, atualizarei a resposta.
MarcG
46
list.removeAll(Collections.singleton(null));

Ele lançará UnsupportedException se você usá-lo em Arrays.asList, pois fornecerá Immutable cópia para que ele não pode ser modificado. Veja abaixo o código. Ele cria uma cópia mutável e não gera nenhuma exceção.

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_
fonte
18

Não eficiente, mas curto

while(tourists.remove(null));
Peter Lawrey
fonte
1
Infelizmente, sua solução foi a única que funcionou para mim ... obrigado!
Pkmmte
simples e rápido
5
@mimrahe o oposto de rápido, na verdade. terrível lento se você tem uma grande lista.
Gewure
18

Se você preferir objetos de dados imutáveis, ou se não quiser ser destrutivo para a lista de entradas, poderá usar os predicados do Guava.

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))
James Kojo
fonte
7
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }
Mat Mannion
fonte
Isso pode ser mais útil quando você precisar excluir elementos enquanto estiver percorrendo. Coincidência é que eu estava anulando os elementos do que tentando usar removeAll(..null..). Obrigado!
Mustafa
É melhor definir os valores como nulo do que remover no final. O batchRemove em removeAll cruza a lista, com um local de leitura e gravação e itera a lista uma vez, movendo a leitura, mas não a gravação, quando atinge um nulo. .remove (), pode ser necessário legitimar a cópia inteira da matriz toda vez que ela for chamada.
Tatarize 27/05
4

Antes do Java 8, você deve usar:

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

Uso pós-Java 8:

tourists.removeIf(Objects::isNull);

A razão aqui é a complexidade do tempo. O problema com matrizes é que uma operação de remoção pode levar O (n) tempo para ser concluída. Realmente em Java, esta é uma cópia da matriz dos elementos restantes sendo movidos para substituir o ponto vazio. Muitas outras soluções oferecidas aqui acionarão esse problema. O primeiro é tecnicamente O (n * m) onde m é 1 porque é um nulo único: então O (n)

Você deve remover All do singleton, internamente ele faz um batchRemove () que possui uma posição de leitura e uma posição de gravação. E itera a lista. Quando atinge um nulo, ele simplesmente itera a posição de leitura em 1. Quando eles são os mesmos que passam, quando são diferentes, ele continua se movendo ao copiar os valores. Então, no final, apara ao tamanho.

Ele efetivamente faz isso 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();
    }
}

O que você pode ver explicitamente é uma operação O (n).

A única coisa que poderia ser mais rápida é se você iterasse a lista dos dois extremos e, quando encontrasse um nulo, defina seu valor igual ao valor encontrado no final e diminua esse valor. E iterou até que os dois valores correspondessem. Você estragaria o pedido, mas reduziria muito o número de valores que você definiu versus os que você deixou sozinho. Qual é um bom método para saber, mas não ajudará muito aqui, pois .set () é basicamente gratuito, mas essa forma de exclusão é uma ferramenta útil para o seu cinto.


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

Embora isso pareça bastante razoável, o .remove () no iterador chama internamente:

ArrayList.this.remove(lastRet);

Qual é novamente a operação O (n) na remoção. Ele faz um System.arraycopy () que novamente não é o que você deseja, se você se preocupa com a velocidade. Isso torna n ^ 2.

Há também:

while(tourists.remove(null));

Qual é O (m * n ^ 2). Aqui, não apenas iteramos a lista. Reiteramos a lista inteira, sempre que correspondermos ao nulo. Em seguida, fazemos n / 2 (médias) operações para executar o System.arraycopy () para executar a remoção. Você poderia literalmente ordenar a coleção inteira entre itens com valores e itens com valores nulos e aparar o final em menos tempo. De fato, isso é verdade para todos os quebrados. Pelo menos em teoria, o system.arraycopy real não é realmente uma operação N na prática. Em teoria, teoria e prática são a mesma coisa; na prática eles não são.

Tatarize
fonte
3

Existe uma maneira fácil de remover todos os nullvalores de collection.Você precisa passar uma coleção contendo null como parâmetro para o removeAll()método

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

yourCollection.removeAll(s1);
shiv
fonte
Isso funcionou melhor para mim. Também permite adicionar facilmente mais de uma entrada na sua "matriz de filtros" que é passada para o método removeAll da coleção original.
3

A Objectsclasse tem um nonNull Predicateque pode ser usado comfilter .

Por exemplo:

tourists.stream().filter(Objects::nonNull).collect(Collectors.toList());
JeffF
fonte
1
Bem-vindo ao Stack Overflow. Ao responder a perguntas, tente adicionar uma explicação do seu código. Volte e edite sua resposta para incluir mais informações.
Tyler
3

Usando o Java 8, você pode fazer isso usando stream()efilter()

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

ou

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

Para mais informações: Java 8 - Streams

Jad Chahine
fonte
1
Esta solução está trabalhando com cópia imutável, ou seja, -> List <String> listOfString = Arrays.asList ("test1", null, "test"); ..... também ! Obrigado
Anurag_BEHS
2

É uma maneira fácil de remover valores nulos padrão do arraylist

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

caso contrário, o valor da string "null" remove da arraylist

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

Eu brinquei com isso e descobri que trimToSize () parece funcionar. Estou trabalhando na plataforma Android para que possa ser diferente.

a blitz
fonte
2
De acordo com o javadoc, trimToSizenão modifica o conteúdo de a ArrayList. Se isso é diferente no Android, provavelmente é um bug.
Fabian #
1

Podemos usar o iterador para remover todos os valores nulos.

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

Usei a interface de fluxo juntamente com a operação de fluxo collect e um método auxiliar para gerar uma nova lista.

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

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

Principalmente estou usando isso:

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

Mas depois que aprendi o Java 8, mudei para isso:

List.removeIf(Objects::isNull);
Maged
fonte
0

Usando o Java 8, isso pode ser realizado de várias maneiras, usando fluxos, fluxos paralelos e 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]

O fluxo paralelo utilizará os processadores disponíveis e acelerará o processo para listas de tamanhos razoáveis. É sempre aconselhável fazer benchmark antes de usar fluxos.

akhil_mittal
fonte
0

Semelhante à resposta @Lithium, mas não gera um erro "A lista não pode conter o tipo nulo":

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