Por que java.util.ArrayList permite adicionar nulo?

41

Eu me pergunto por que java.util.ArrayListpermite adicionar null. Existe algum caso em que eu gostaria de adicionar nulla um ArrayList?

Estou fazendo essa pergunta porque, em um projeto, tivemos um bug em que algum código estava sendo adicionado nullao arquivo ArrayListe era difícil identificar onde estava o erro. Obviamente, um NullPointerExceptionfoi lançado, mas não até outro código tentar acessar o elemento. O problema foi como localizar o código que adicionou o nullobjeto. Teria sido mais fácil se houvesse ArrayListuma exceção no código em que os elementos estavam sendo adicionados.

Alfredo Osorio
fonte
12
O Projeto Guava tem uma página bastante interessante sobre esse tópico (eles não permitem a nullmaioria de suas coleções).
Joachim Sauer
6
Eu acredito que as respostas dadas aqui cobrem bem a pergunta. Talvez uma coisa que deva ser mencionada seja: não considere tudo o que há no JDK como azevinho e perfeito e depois bata com a cabeça tentando entender por que é tão "perfeito". Algumas coisas são erros (honestos, IMHO), que permaneceram lá devido à compatibilidade com versões anteriores e é isso. Até os criadores de Java admitem, basta ler os livros de Joshua Bloch para ver sua crítica a certas APIs Java. De qualquer forma, sua pergunta se resume ao clima: não há uma maneira mais elegante de capturar o NPE em Java. A resposta é não, mas deve haver.
Shivan Dragon
2
Você pode fornecer mais informações sobre por que isso não deve ser permitido? Se é apenas uma questão de gosto, o menos restritivo deve ser preferido.
Mare Infinitus
A resposta simples é exatamente essa nullé a maneira padrão de representar dados ausentes em Java, quer você goste ou não. De fato, muitas pessoas não gostam e fazem disso um argumento para coisas funcionais no estilo de programação. A resposta mais votada não faz sentido / não captura a essência do problema.
XJI
@JIXiang Você está simplificando demais o que nullé. "Missing" é apenas uma das várias interpretações possíveis de null. Outras interpretações válidas podem ser "Desconhecido", "Não aplicável" ou "Não inicializado". O que nullrepresenta depende da aplicação. Como a comunidade Python diria: "Diante da ambiguidade, recuse a tentação de adivinhar". Recusar manter nulo em um contêiner perfeitamente capaz de fazer isso seria apenas isso - um palpite.
Riwalk

Respostas:

34

Essa decisão de design aparece principalmente conduzida pela nomeação.

O nome ArrayList sugere a leitura de uma funcionalidade semelhante às matrizes - e é natural que os designers do Java Collections Framework esperem que a grande maioria dos usuários da API conte com ele funcionando de maneira semelhante às matrizes.

Isso, em particular, envolve o tratamento de elementos nulos. Usuário da API sabendo que abaixo funciona bem:

array[0] = null; // NPE won't happen here

ficaria surpreso ao descobrir se código semelhante para ArrayList lançaria NPE:

arrayList.set(0, null); // NPE => WTF?

O raciocínio como acima é apresentado no tutorial do JCF, enfatizando pontos que sugerem uma similaridade próxima entre ArrayList e matrizes simples:

ArrayList ... oferece acesso posicional em tempo constante e é simplesmente rápido ...

Se você deseja que uma implementação da Lista não permita nulos, é melhor chamar isso NonNullableArrayListou algo assim para evitar confundir os usuários da API.


Nota lateral: há uma discussão auxiliar nos comentários abaixo, além de considerações adicionais que sustentam o raciocínio apresentado aqui.

mosquito
fonte
12
Esta explicação não é convincente, considerando que LinkedList também suporta nullentradas de lista.
Stephen C
12
Sim ... mas uma explicação mais simples e mais plausível é que permitir nullentradas é útil em muitos casos.
Stephen C
7
ArrayList não é chamado assim porque imita uma matriz. É chamado assim porque é uma lista implementada como uma matriz. Assim como um TreeMap não se comporta como uma árvore.
Florian F
11
Esta resposta, IMO, está completamente errada. Nulos são permitidos porque em Java, para melhor ou para pior, (IMO, pior), o nulo é muito usado para representar valores não inicializados ou ausentes. Como conseguiu mais votos e o cheque "Accept"? Sério, estou perdendo a fé no StackOverflow atualmente.
user949300
8
Esta resposta está completamente errada. Isso é apenas uma suposição não suportada sobre a permissão de nulos em uma implementação de lista. Aqui está um ajuntamento de coleções Java permitindo / nulos disallowing; observe como isso não tem nada a ver com a semelhança com matrizes.
9788 Andres F.
32

Nulo pode ser um valor válido para um elemento de uma lista. Digamos que sua lista contenha elementos que representem alguns dados opcionais sobre uma lista de usuários e seja armazenada na mesma ordem que os usuários. Se os dados extras forem preenchidos, sua lista conterá os dados adicionais, caso contrário, o slot correspondente a um usuário será nulo. (Tenho certeza de que existem exemplos melhores, mas você entendeu)

se você não quiser permitir que nulos sejam adicionados, você pode agrupar a lista de matrizes com seu próprio invólucro, lançado quando o nulo foi adicionado.

Sam Holder
fonte
2
Esse parece ser um mau exemplo de por que os nulos devem ser permitidos - há maneiras melhores de representar dados opcionais do que usar valores nulos em uma lista.
casablanca
@casablanca sim concordo totalmente, não é um ótimo exemplo.
Sam Titular
Não consigo encontrar um bom motivo para um ArrayList conter nulos, além de más surpresas para o próximo desenvolvedor "descobri-lo": /
AndreasScheinert 4/12/12
7
@casablanca Embora eu concorde com você que nulos devem ser evitados por representar dados opcionais, em Java, para melhor ou para pior, isso é "tradicional".
user949300
2
Um caso de uso sólido: você deseja passar os parâmetros para alguma função dinâmica como uma lista. Você tem várias funções, cada uma com um conjunto diferente de parâmetros, portanto, você precisa de uma matriz de tamanho dinâmico e sempre pode passar nulo como um argumento de função válido!
Falco
19

ArrayListpermite nulo por design. É intencional. Do javadoc :

"[ArrayList é uma] implementação redimensionável da interface List. Implementa todas as operações opcionais da lista e permite todos os elementos, incluindo nulos ."

A resposta para "por que" é que, se isso não acontecesse, o ArrayList não seria utilizável nos casos em que é necessário colocar um nullna lista. Por outro lado, você pode impedir que um ArrayList contenha valores nulos testando valores antes de incluí-los ou usando um wrapper que impede que isso aconteça.

Existe algum caso em que eu gostaria de adicionar nulo a um ArrayList?

Obviamente, qualquer caso em que nulltenha um significado distinto. Por exemplo, pode significar que o valor em uma determinada posição na lista não foi inicializado ou fornecido.

Seria mais fácil se o ArrayList tivesse lançado uma exceção no código em que os elementos estavam sendo adicionados.

Você pode implementar esse comportamento facilmente criando uma classe de wrapper. No entanto, esse não é o comportamento que a maioria dos programadores / aplicativos precisa.

Stephen C
fonte
8

Existe algum caso em que eu gostaria de adicionar nulo a um ArrayList?

Claro, e a pré-alocação? Você quer ArrayListalgumas coisas que ainda não pode criar porque não possui informações suficientes. Só porque você não consegue pensar em uma razão pela qual alguém pode querer fazer algo não a torna uma má idéia. Tanto faz. (Agora, tenho certeza de que alguém aparecerá e dirá que você deve ter objetos vazios que atendam a algum padrão sobre o qual leram em algum blog obscuro e que os programadores realmente possam escrever programas sem nunca usar ifdeclarações, blá, blá).

Se vocês têm um contrato que nunca deve haver nulls em um contêiner, é da sua responsabilidade garantir que o contrato seja cumprido, provavelmente de maneira mais apropriada, afirmando. Provavelmente levaria no máximo 10 linhas de código. Java torna incrivelmente fácil para você fazer esse tipo de coisa. O JDK não consegue ler sua mente.

James
fonte
1
Seu argumento de pré-alocação é inválido, porque isso já está incorporado no ArrayList com o ensureCapacity(int minCapacity)método e o ArrayList(int initialCapacity)construtor.
Philipp
7
@ Philipp Que valores ele colocará nesse espaço?
James
A escolha óbvia é colocar um nullnas entradas pré-alocadas (mas ainda não utilizadas ) do ArrayList; mas podemos deixar ensureCapacityfazer isso e ainda não permitir outras funções, como setfazê-lo. As razões apresentadas em outros lugares parecem mais fortes.
David K
@ David: Faz sentido dizer que deve ser possível setum item com qualquer valor que possa ser retornado get. Mesmo se uma tentativa de normal a getum item para que o espaço tinha sido atribuída, mas nunca escrito deve lançar uma exceção em vez de retornar null, ainda seria útil ter um par de métodos que permitiria list1.setOrEraseIfNull(index, list2.getOrReturnNull(index))ao invés de exigirif (list2.valueSet(index)) list1.set(index, list2.get(index)); else list1.unset(index);
supercat
1
@DavidK: Tentar ler uma entrada que foi alocada, mas ainda não foi gravada deve gerar nulo ou lançar uma exceção; é mais fácil fazê-lo retornar nulo.
Supercat
4

Isso parece ser mais uma questão filosófica (de software) aqui.

ArrayList como uma classe de utilitário foi projetada para ser útil em um amplo contexto de possíveis casos de uso.

Ao contrário de sua alegação oculta de que a aceitação de nulo como um valor válido deve ser desencorajada, há muitos exemplos em que o valor nulo é perfeitamente legal.

O motivo mais importante nullé o do not knowequivalente a qualquer tipo de referência, incluindo os tipos de estrutura. Isso implica que nulo não pode ser substituído em nenhum caso por uma versão mais fluente do nothingvalor.

Então, digamos que você armazene os números inteiros 1, 3, 7 na sua lista de matrizes no índice correspondente: devido a alguns cálculos, você deseja obter o elemento no índice 5, então o que sua matriz deve retornar é: "nenhum valor armazenado". Isso pode ser alcançado retornando null ou um NullObject . Na maioria dos casos, o retorno do valor nulo incorporado é expressivo o suficiente. Depois de chamar um método que pode retornar nulo e usar seu valor de retorno, uma verificação do valor retornado contra nulo é bastante comum no código moderno.

Mare Infinitus
fonte
4

A declaração

File f = null;

é válido e útil (acho que não preciso explicar o porquê). Da mesma forma, é útil ter uma coleção de arquivos, alguns dos quais podem ser nulos.

List<File> files = new ArrayList<File>();
// use my collection of files
// ....
// not using this one anymore:
files.set(3, null);

A utilidade de coleções que podem conter objetos nulos procede diretamente da utilidade de objetos que podem ser nulos. É realmente tão simples como isso.

código x
fonte
2

É mais fácil aceitar tudo, do que ser restritivo e, em seguida, tente abrir seu design após o fato.

Por exemplo, e se o oracle / sun fornecesse apenas NonNullableArrayList, mas você quisesse adicionar um Null à sua lista. Como você faria? Você provavelmente teria que criar um objeto totalmente diferente; não seria possível estender o NonNullableArrayList. Em vez disso, se você tiver um ArrayList que aceita tudo, poderá estendê-lo facilmente e substituir o add, onde ele não aceita valores nulos.

NiteRain
fonte
2
Discordou. Permitir demais é mais difícil de corrigir, uma vez que o mau funcionamento é amplamente disseminado. Se os designers de linguagem permitissem nulos em um estágio posterior, isso não interromperia os programas existentes, mas o contrário não é verdadeiro.
Andres F.
2
Mas eu concordo. Trata-se de maximizar a funcionalidade. Você pode usar uma lista que aceita nulos, mesmo que você não precise deles. Você não pode usar uma lista que recuse nulos se precisar de nulos.
Florian F
-1

Utilidade de nulo?

Muito quando você usa uma Lista de listas para modelar uma placa 2D da vida real com posições diferentes. Metade dessas posições pode estar vazia (em coordenadas aleatórias) enquanto as preenchidas não representam um int ou String simples, mas são representadas por um objeto complexo. Se você deseja manter as informações posicionais, preencha os espaços vazios com nulo, para que sua lista.get (x) .get (y)não leva a surpresas desagradáveis. Para combater as exceções nulas, você pode procurar nulas (muito populares) ou pode usar um opcional (que acho útil neste caso). A alternativa seria preencher os espaços vazios com objetos "lixo" que podem levar a todos os tipos de bagunça no caminho. Se sua verificação nula falhar ou for esquecida em algum lugar, o Java informará você. Por outro lado, um objeto de espaço reservado "lixo" que não é verificado corretamente pode passar despercebido.

Nanobody
fonte