¿Por qué java.util.ArrayList permite agregar nulo?

41

Me pregunto por qué java.util.ArrayListpermite agregar null. ¿Hay algún caso en el que me gustaría agregar nulla un ArrayList?

Me hago esta pregunta porque en un proyecto que teníamos un error en algún código se agregó nulla la ArrayListy era difícil de detectar dónde estaba el error. Obviamente NullPointerExceptionse arrojó un pero no hasta que otro código intentó acceder al elemento. El problema era cómo localizar el código que agregaba el nullobjeto. Hubiera sido más fácil si hubiera ArrayListuna excepción en el código donde se agregaban los elementos.

Alfredo Osorio
fuente
12
The Guava Project tiene una página bastante interesante sobre ese tema (no permiten nullen la mayoría de sus colecciones).
Joachim Sauer
66
Creo que las respuestas dadas aquí cubren bien la pregunta. Tal vez una cosa que debería mencionarse es: no tome todo lo que está en el JDK como holly y perfecto, y luego golpee su cabeza tratando de entender por qué es tan "perfecto". Algunas cosas son errores (honestos, en mi humilde opinión), que permanecieron allí debido a la compatibilidad con versiones anteriores y eso es todo. Incluso los creadores de Java lo admiten, solo lea los libros de Joshua Bloch para ver su crítica de ciertas API de Java. En cualquier caso, su pregunta se reduce al clima, no hay una forma más elegante de atrapar NPE en Java. La respuesta es no, pero debería haberla.
Shivan Dragon
2
¿Puede proporcionar más información sobre por qué no debería permitirse? Si es solo cuestión de gustos, se debe preferir el menos restrictivo.
Mare Infinitus
La respuesta simple es que esa nulles la forma predeterminada de representar los datos faltantes en Java, te guste o no. De hecho, a mucha gente no le gusta y lo convierte en un argumento para cosas de estilo de programación funcional. La respuesta más votada no tiene sentido / no captura la esencia del problema.
xji
@JIXiang Estás simplificando demasiado lo que nulles. "Missing" es solo una de varias interpretaciones potenciales de null. Otras interpretaciones válidas pueden ser "Desconocido", "No aplicable" o "No inicializado". Lo que nullrepresenta depende de la aplicación. Como diría la comunidad de Python: "Ante la ambigüedad, rechace la tentación de adivinar". Negarse a mantener nulo en un contenedor que sea perfectamente capaz de hacerlo sería solo eso, una suposición.
Riwalk

Respuestas:

34

Esta decisión de diseño parece principalmente impulsada por los nombres.

Name ArrayList sugiere al lector una funcionalidad similar a las matrices , y es natural que los diseñadores de Java Collections Framework esperen que la gran mayoría de los usuarios de API confíen en que funcione de manera similar a las matrices.

Esto en particular, implica el tratamiento de elementos nulos. El usuario de la API sabe que a continuación funciona bien:

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

estaría bastante sorprendido de descubrir si un código similar para ArrayList arrojaría NPE:

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

El razonamiento como el anterior se presenta en los puntos de énfasis del tutorial JCF que sugieren una gran similitud entre ArrayList y las matrices simples:

ArrayList ... ofrece acceso posicional en tiempo constante y es simplemente rápido ...

Si desea una implementación de Lista que no permita nulos, será mejor que se llame así NonNullableArrayListo algo así, para evitar confundir a los usuarios de API.


Nota al margen hay una discusión auxiliar en los comentarios a continuación, junto con consideraciones adicionales que respaldan el razonamiento aquí expuesto.

mosquito
fuente
12
Esta explicación no es convincente, teniendo en cuenta que LinkedList también admite nullentradas de lista.
Stephen C
12
Sí ... pero una explicación más simple y (IMO) más plausible es que permitir nullentradas es útil en muchos casos.
Stephen C
77
ArrayList no se llama así porque imita una matriz. Se llama así porque es una lista implementada como una matriz. Así como un TreeMap no se comporta como un árbol.
Florian F
11
Esta respuesta, OMI, es simplemente errónea. Los valores nulos están permitidos porque en Java, para bien o para mal, (IMO, peor) el nulo se usa mucho para representar valores no inicializados o faltantes. ¿Cómo obtuvo la mayoría de los votos y el cheque de "aceptar"? En serio, realmente estoy perdiendo la fe en StackOverflow en estos días.
user949300
8
Esta respuesta es completamente incorrecta. Esto es solo una conjetura no admitida sobre los nulos permitidos en una implementación de lista. Aquí hay un resumen de las colecciones de Java que permiten / no permiten nulos; observe cómo no tiene nada que ver con la similitud con las matrices.
Andres F.
32

Nulo puede ser un valor válido para un elemento de una lista. Digamos que su lista contiene elementos que representan algunos datos opcionales sobre una lista de usuarios y se almacena en el mismo orden que los usuarios. Si se rellenan los datos adicionales, su lista contendrá los datos adicionales; de lo contrario, el espacio correspondiente a un usuario será nulo. (Estoy seguro de que hay mejores ejemplos, pero entiendes la idea)

si no desea permitir que se agreguen nulos, puede ajustar la lista de matrices con su propio contenedor que arrojó cuando se agregó nulo.

Sam Holder
fuente
2
Ese parece ser un mal ejemplo de por qué deberían permitirse los nulos: hay mejores formas de representar datos opcionales que usar valores nulos en una lista.
casablanca
@casablanca sí, totalmente de acuerdo, no es un gran ejemplo.
Sam Holder
No puedo encontrar una buena razón para que una ArrayList contenga nulos, aparte de las malas sorpresas para que el próximo desarrollador la "descubra": /
AndreasScheinert
77
@casablanca Si bien estoy de acuerdo con usted en que los valores nulos deben evitarse para representar datos opcionales, en Java, para bien o para mal, eso es "tradicional".
user949300
2
Un caso de uso sólido: desea pasar los parámetros a alguna función dinámica como una lista. ¡Tiene varias funciones, cada una con un conjunto diferente de parámetros, por lo que necesita una matriz de tamaño dinámico y siempre puede pasar nulo como un argumento de función válido!
Falco
19

ArrayListpermite nulo por diseño. Es intencional. Desde el javadoc :

"[ArrayList es una] implementación de matriz redimensionable de la interfaz List. Implementa todas las operaciones de lista opcionales y permite todos los elementos, incluido nulo ".

La respuesta a "por qué" es que si no fuera así, ArrayList no sería utilizable en los casos en que sea necesario poner un nullen la lista. Por el contrario, puede evitar que un ArrayList contenga valores nulos probando los valores antes de agregarlos o utilizando un contenedor que evite que esto suceda.

¿Hay algún caso en el que me gustaría agregar nulo a una ArrayList?

Obviamente, cualquier caso donde nulltiene un significado distinto. Por ejemplo, podría significar que el valor en una posición dada en la lista no se ha inicializado o suministrado.

Hubiera sido más fácil si ArrayList hubiera arrojado una excepción en el código donde se agregaban los elementos.

Puede implementar fácilmente ese comportamiento creando una clase de contenedor. Sin embargo, este no es el comportamiento que necesitan la mayoría de los programadores / aplicaciones.

Stephen C
fuente
8

¿Hay algún caso en el que me gustaría agregar nulo a una ArrayList?

Claro, ¿qué pasa con la preasignación? Desea una ArrayListde las cosas que aún no puede crear porque no tiene suficiente información. El hecho de que no se te ocurra una razón por la que alguien quiera hacer algo no lo convierte en una mala idea. Lo que sea. (Ahora, estoy seguro de que alguien vendrá y dirá que en su lugar debería tener objetos vacíos que cumplan con algún patrón sobre el que leyeron en algún blog oscuro y que los programadores realmente deberían poder escribir programas sin usar nunca ifdeclaraciones, bla, bla).

Si ustedes tienen un contrato que nunca debe haber nulls en un contenedor, entonces depende de usted asegurarse de que se cumpla el contrato, probablemente lo más apropiado al afirmarlo. Probablemente te hubiera tomado un máximo de 10 líneas de código. Java te hace increíblemente fácil hacer este tipo de cosas. El JDK no puede leer tu mente.

James
fuente
1
Su argumento de preasignación no es válido, porque esto ya está integrado en ArrayList con el ensureCapacity(int minCapacity)método y el ArrayList(int initialCapacity)constructor.
Philipp
77
@Philipp ¿Qué valores pondrá en ese espacio?
James
La opción obvia es poner un nullen las entradas preasignadas (pero aún no utilizadas ) de ArrayList; pero podemos dejar de ensureCapacityhacer eso y, sin embargo, no permitir otras funciones como sethacerlo. Las razones dadas en otros lugares parecen más fuertes.
David K
@DavidK: tiene sentido decir que debería ser posible que setun artículo tenga cualquier valor que pueda devolver get. Incluso si un intento normal de getun elemento para el que se ha asignado espacio pero nunca escrito debería arrojar una excepción en lugar de regresar null, aún sería útil tener un par de métodos que permitirían en list1.setOrEraseIfNull(index, list2.getOrReturnNull(index))lugar de requeririf (list2.valueSet(index)) list1.set(index, list2.get(index)); else list1.unset(index);
supercat
1
@DavidK: Intentar leer una entrada que se ha asignado pero que aún no se ha escrito debe dar como resultado nulo o lanzar una excepción; es más fácil que devuelva nulo.
supercat
4

Esto parece ser más una pregunta filosófica (software) aquí.

ArrayList como una clase de utilidad está diseñada para ser útil en un amplio contexto de posibles casos de uso.

Contrariamente a su afirmación oculta de que no se debe aceptar aceptar nulo como valor válido, hay muchos ejemplos en los que el valor nulo es perfectamente legal.

La razón más importante es que nulles el do not knowequivalente de cualquier tipo de referencia, incluidos los tipos de marco. Esto implica que nulo no puede ser reemplazado en ningún caso por una versión más fluida del nothingvalor.

Entonces, supongamos que almacena los números enteros 1, 3, 7 en su lista de arrays en su índice correspondiente: debido a algunos cálculos, desea obtener el elemento en el índice 5, por lo que su matriz debería devolver: "ningún valor almacenado". Esto podría lograrse devolviendo nulo o un objeto nulo . En la mayoría de los casos, devolver el valor nulo incorporado es suficientemente expresivo. Después de llamar a un método que puede devolver nulo y usar su valor de retorno, una verificación del valor devuelto contra nulo es bastante común en el código moderno.

Mare Infinitus
fuente
4

La declaración

File f = null;

es válido y útil (no creo que necesite explicar por qué). También es útil tener una colección de archivos, algunos de los cuales pueden ser nulos.

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

La utilidad de las colecciones que pueden contener objetos nulos procede directamente de la utilidad de los objetos que pueden ser nulos. Es realmente así de simple.

código x
fuente
2

Es más fácil aceptar todo, que ser restrictivo y luego intentar abrir su diseño después del hecho.

Por ejemplo, ¿qué pasaría si oracle / sun proporcionara solo NonNullableArrayList, pero quisiera poder agregar un Nulo a su lista? ¿Como lo harias? Probablemente tendría que crear un objeto completamente diferente, no podría usar extender la NonNullableArrayList. En cambio, si tiene una ArrayList que toma todo, podría extenderla fácilmente y anular la adición, donde no acepta valores nulos.

NiteRain
fuente
2
En desacuerdo Permitir demasiado es más difícil de corregir una vez que la mala función ha tenido un uso generalizado. Si los diseñadores de lenguaje permitieran nulos en una etapa posterior, no rompería los programas existentes, pero lo contrario no es cierto.
Andres F.
2
Pero estoy de acuerdo. Se trata de maximizar la funcionalidad. Puede usar una lista que acepte valores nulos aunque no los necesite. No puede usar una lista que rechace los nulos si los necesita.
Florian F
-1

Utilidad de nulo?

Mucho más cuando vas con una Lista de Listas para modelar una placa de la vida real en 2D con diferentes posiciones. La mitad de esas posiciones podrían estar vacías (en coordenadas aleatorias) mientras que las ocupadas no representan un int o String simple, sino que están representadas por un objeto complejo. Si desea mantener la información posicional, debe llenar los espacios vacíos con nulos para que su lista.get (x) .get (y)no conduce a sorpresas desagradables. Para contrarrestar las excepciones nulas, puede verificar nulo (muy popular) o puede usar un Opcional (que me parece útil en este caso). La alternativa sería llenar los espacios vacíos con objetos "basura" que pueden conducir a todo tipo de desorden en el camino. Si su cheque nulo falla o se olvida en alguna parte, Java se lo informará. Por otro lado, un objeto de marcador de posición "basura" que no se verifica correctamente puede pasar desapercibido.

Nanocuerpo
fuente