Collections.emptyList () vs. nueva instancia

241

En la práctica, es mejor devolver una lista vacía como esta :

return Collections.emptyList();

O como esta :

return new ArrayList<Foo>();

¿O depende completamente de lo que va a hacer con la lista devuelta?

mre
fuente

Respuestas:

300

La principal diferencia es que Collections.emptyList()devuelve una lista inmutable , es decir, una lista a la que no puede agregar elementos. (Lo mismo se aplica a lo List.of()introducido en Java 9.)

En los raros casos en que no desea modificar la lista devuelta, Collections.emptyList()y List.of()por lo tanto son no unas buenas opciones.

Yo diría que devolver una lista inmutable está perfectamente bien (e incluso la forma preferida) siempre que el contrato (documentación) no establezca de manera explícita de manera diferente.


Además, emptyList() podría no crear un nuevo objeto con cada llamada.

Las implementaciones de este método no necesitan crear un objeto List separado para cada llamada. Es probable que el uso de este método tenga un costo comparable al uso del campo con el mismo nombre. (A diferencia de este método, el campo no proporciona seguridad de tipo).

La implementación de emptyListlooks de la siguiente manera:

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

Entonces, si su método (que devuelve una lista vacía) se llama con mucha frecuencia, este enfoque puede incluso brindarle un rendimiento ligeramente mejor tanto en CPU como en memoria.

aioobe
fuente
44
Entonces, ¿ Collections.emptyList()sería más adecuado para, digamos, verificación de errores y similares?
mre
1
Los clientes API no obtendrán NullPointerExceptionregresando en Collections.emptyList()lugar de null.
realPK
@PK_J hace un punto importante. Collections.emptyList()es iterable y devuelve una longitud, por lo que puede usarse para bucles sin que se produzca una excepción.
ndm13
¿Qué hay de usar List.of()?
44
@AJW, sí. Pero en comparación con, digamos, new ArrayList<>()también aclara la decisión de diseño; los elementos no se agregarán a esta lista.
aioobe
51

Comenzando con Java 5.0, puede especificar el tipo de elemento en el contenedor:

Collections.<Foo>emptyList()

Estoy de acuerdo con las otras respuestas de que para los casos en que desea devolver una lista vacía que permanezca vacía, debe usar este enfoque.

Paul Jackson
fuente
38
A partir de Java 7, puede dejar que el compilador deduzca el parámetro de tipo de la invocación del método genérico del tipo de destino:List<Foo> list = Collections.emptyList()
Paul Jackson
28

Collections.emptyList es inmutable, por lo que hay una diferencia entre las dos versiones, por lo que debe considerar a los usuarios del valor devuelto.

La devolución new ArrayList<Foo>siempre crea una nueva instancia del objeto, por lo que tiene un costo adicional muy pequeño asociado, lo que puede darle una razón para usarlo Collections.emptyList. Me gusta usar emptyListsolo porque es más legible.

Jeff Foster
fuente
14

Pero ten cuidado. Si regresa Collections.emptyList()y luego intenta hacer algunos cambios con él como add()o algo así, tendrá un UnsupportedOperationException()porque Collections.emptyList()devuelve un objeto inmutable.

Sergey Frolov
fuente
7

Iría con Collections.emptyList()si la lista devuelta no se modifica de ninguna manera (ya que la lista es inmutable), de lo contrario iría con la opción 2.

El beneficio de Collections.emptyList()es que la misma instancia estática se devuelve cada vez y, por lo tanto, no se produce una creación de instancia para cada llamada.

S73417H
fuente
3

Use Collections.emptyList () si desea asegurarse de que la lista devuelta nunca se modifique. Esto es lo que se devuelve al llamar a emptyList ():

/**
 * The empty list (immutable). 
 */
public static final List EMPTY_LIST = new EmptyList();
Atul
fuente
Llegué aquí tratando de averiguar si llamar Collections.emptyList()tenía un costo de construcción. Ver los detalles de implementación (aunque probablemente no sea el mismo en todas las JVM) confirma que no es así. @ Atul, ¿de qué JVM es esto?
wjl
2

Las respuestas dadas enfatizan el hecho de que emptyList()devuelve un valor inmutable Listpero no dan alternativas. Los ArrayList(int initialCapacity)casos especiales de Constructor , 0por lo que regresar en new ArrayList<>(0)lugar de new ArrayList<>()también podría ser una solución viable:

/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

[...]

/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

(fuentes de Java 1.8.0_72)

René
fuente
No estoy de acuerdo con tu enfoque. Ahorra un poco de memoria y CPU en la inicialización, pero si la lista que devolvió alguna vez se modifica, pierde ese tiempo cuando la lista reasigna una nueva matriz. Si se agregan muchos elementos a la lista a lo largo del tiempo, esto podría acumularse en un cuello de botella de rendimiento debido a la tasa de crecimiento mucho más lenta . Prefiero apegarme a la convención de una lista vacía no modificable o una lista utilizable y modificable.
Patrick M
1
Como traté de enfatizar con mi redacción ( podría ser viable ): todo depende de su caso de uso. En general , devolvería Colecciones mutables o inmutables, no una mezcla dependiendo de si están vacías o no. Y para contrarrestar el "reclamo mucho más lento": esta es la implementación actual.
René
Oh hombre, mírame citando las versiones principales de JDK 2 desactualizadas. Entonces java8 evita el cuello de botella por completo al saltar a la capacidad predeterminada desde un tamaño inicial de 0. Lo siento, estaba tan equivocado.
Patrick M