Inicialización de un ArrayList en una línea

Resuelto Macarse asked hace 15 años • 35 respuestas

Quería crear una lista de opciones para fines de prueba. Al principio hice esto:

ArrayList<String> places = new ArrayList<String>();
places.add("Buenos Aires");
places.add("Córdoba");
places.add("La Plata");

Luego, refactoricé el código de la siguiente manera:

ArrayList<String> places = new ArrayList<String>(
    Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));

¿Hay una mejor manera de hacer esto?

Macarse avatar Jun 17 '09 11:06 Macarse
Aceptado

Sería más sencillo si simplemente lo declarara como List: ¿tiene que ser un ArrayList?

List<String> places = Arrays.asList("Buenos Aires", "Córdoba", "La Plata");

O si tienes un solo elemento:

List<String> places = Collections.singletonList("Buenos Aires");

Esto significaría que placeses inmutable (intentar cambiarlo provocará que UnsupportedOperationExceptionse genere una excepción).

Para hacer una lista mutable que sea concreta, ArrayListpuede crear una ArrayLista partir de la lista inmutable:

ArrayList<String> places = new ArrayList<>(Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));

E importe el paquete correcto:

import java.util.Arrays;
Tom avatar Jun 17 '2009 04:06 Tom

En realidad, probablemente la "mejor" forma de inicializar ArrayListes el método que escribiste, ya que no es necesario crear uno nuevo Listde ninguna manera:

ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");

El problema es que es necesario escribir bastante para hacer referencia a esa listinstancia.

Existen alternativas, como crear una clase interna anónima con un inicializador de instancia (también conocido como "inicialización de doble llave"):

ArrayList<String> list = new ArrayList<String>() {{
    add("A");
    add("B");
    add("C");
}};

Sin embargo, no me gusta mucho ese método porque lo que obtienes es una subclase que ArrayListtiene un inicializador de instancia, y esa clase se crea solo para crear un objeto; eso me parece un poco excesivo.

Lo que hubiera sido bueno sería que se aceptara la propuesta de Collection Literals para Project Coin (estaba previsto introducirla en Java 7, pero tampoco es probable que forme parte de Java 8):

List<String> list = ["A", "B", "C"];

Lamentablemente, no le ayudará aquí, ya que inicializará un inmutable Listen lugar de un ArrayList, y además, aún no está disponible, si es que alguna vez lo estará.

coobird avatar Jun 17 '2009 04:06 coobird

la respuesta sencilla

Java 9 o posterior:

List<String> strings = List.of("foo", "bar", "baz");

List.of(...)le dará un inmutable List , por lo que no se puede cambiar.
Que es lo que desea en la mayoría de los casos en los que lo completa previamente.

Esto no permite nullelementos.

Java 8 o anterior:

List<String> strings = Arrays.asList("foo", "bar", "baz");

Arrays.asList(...)le dará un List* respaldado por una matriz, por lo que no puede cambiar la longitud.
Pero puedes llamar List.set(...), por lo que sigue siendo mutable .

Esto sí permite nullelementos.

* Detalle de implementación: Es una clase privada anidada dentro de java.util.Arrays, llamada ArrayList,
que es una clase diferente de java.util.ArrayList, aunque sus nombres simples son los mismos.

Importación estática

Puedes acortar Arrays.asListaún más Java 8 con una importación estática:

import static java.util.Arrays.asList;  
...
List<String> strings = asList("foo", "bar", "baz");

Cualquier IDE * moderno le sugerirá y hará esto por usted.

No recomiendo importar estáticamente el List.ofmétodo como just of, porque es confuso.

* Por ejemplo, en IntelliJ IDEA presionas Alt+Entery seleccionasStatic import method...

Usando Streams

¿Por qué tiene que ser un List?
Con Java 8 o posterior puedes usar uno Streamque es más flexible:

Stream<String> strings = Stream.of("foo", "bar", "baz");

Puedes concatenar Streams:

Stream<String> strings = Stream.concat(Stream.of("foo", "bar"),
                                       Stream.of("baz", "qux"));

O puedes ir de a Streama a List:

import static java.util.stream.Collectors.toList;
...
var strings = Stream.of("foo", "bar", "baz").toList(); // Java 16

List<String> strings = Stream.of("foo", "bar", "baz").collect(toList()); // Java 8

Pero preferiblemente, simplemente use el archivo Streamsin recopilarlo en un archivo List.

Si necesita específicamente un java.util.ArrayList*

Si desea rellenar previamente un an ArrayList y agregarlo después, utilice

List<String> strings = new ArrayList<>(List.of("foo", "bar"));

o en Java 8 o anterior:

List<String> strings = new ArrayList<>(asList("foo", "bar"));

o usando Stream:

import static java.util.stream.Collectors.toCollection;

List<String> strings = Stream.of("foo", "bar")
                             .collect(toCollection(ArrayList::new));

Luego puedes agregarle después de la construcción:

strings.add("baz");

Pero nuevamente, es mejor usarlo Streamdirectamente en lugar de recopilarlo en un archivo List.

* Probablemente no necesites específicamente un archivo ArrayList. Para citar JEP 269 :

Existe un pequeño conjunto de casos de uso para inicializar una instancia de colección mutable con un conjunto de valores predefinido. Generalmente es preferible tener esos valores predefinidos en una colección inmutable y luego inicializar la colección mutable mediante un constructor de copia.

(el énfasis es mío)

Programa a interfaces, no a implementaciones.

Dijiste que declaraste la lista como ArrayListen tu código, pero solo debes hacerlo si estás usando algún miembro que ArrayListno esté en List.

Lo que probablemente no estés haciendo.

Por lo general, debería declarar las variables mediante la interfaz más general que vaya a utilizar (por ejemplo Iterable, Collectiono List) e inicializarlas con la implementación específica (por ejemplo ArrayList, LinkedListo Arrays.asList()).

De lo contrario, estará limitando su código a ese tipo específico y será más difícil cambiarlo cuando lo desee.

Por ejemplo, si estás pasando un ArrayLista void method(...):

// Iterable if you just need iteration, for (String s : strings):
void method(Iterable<String> strings) { 
    for (String s : strings) { ... } 
}

// Collection if you also need .size(), .isEmpty(), or .stream():
void method(Collection<String> strings) {
    if (!strings.isEmpty()) { strings.stream()... }
}

// List if you also need random access, .get(index):
void method(List<String> strings) {
    strings.get(...)
}

// Don't declare a specific list implementation
// unless you're sure you need it:
void method(ArrayList<String> strings) {
    ??? // You don't want to limit yourself to just ArrayList
}

Otro ejemplo sería declarar siempre la variable an InputStreamaunque generalmente sea a FileInputStreamo a BufferedInputStream, porque algún día, pronto, usted o alguien más querrá usar algún otro tipo de InputStream.

Christoffer Hammarström avatar Sep 09 '2010 12:09 Christoffer Hammarström

Si necesita una lista simple de tamaño 1:

List<String> strings = new ArrayList<String>(Collections.singletonList("A"));

Si necesita una lista de varios objetos:

List<String> strings = new ArrayList<String>();
Collections.addAll(strings,"A","B","C","D");
Randyaa avatar Aug 30 '2011 04:08 Randyaa