Pregunta Inicialización de una ArrayList en una línea


Quiero 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 refactoré 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?


2157
2018-06-17 04:10


origen


Respuestas:


En realidad, probablemente la "mejor" manera de inicializar el ArrayList es el método que escribiste, ya que no necesita crear un nuevo List de cualquier manera:

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

El problema es que se requiere bastante tipeo para referirse a ese list ejemplo.

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

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

Sin embargo, no soy muy aficionado a ese método porque con lo que terminas es una subclase de ArrayList que tiene un inicializador de instancia, y esa clase se crea solo para crear un objeto, eso me parece demasiado exagerado.

Lo que hubiera sido bueno fue si el Propuesta de Collection Literals para Project Coin fue aceptado (estaba programado para ser introducido en Java 7, pero tampoco es probable que sea parte de Java 8):

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

Desafortunadamente no te ayudará aquí, ya que inicializará un inmutable List en lugar de un ArrayListy, además, aún no está disponible, si alguna vez lo será.


1624
2018-06-17 04:13



Sería más simple si solo declaras como una List - ¿Tiene que ser un ArrayList?

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

O si solo tiene un elemento:

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

Esto significaría que places es inmutable (Tratar de cambiarlo causará una UnsupportedOperationException excepción que se arrojará).

Para hacer una lista mutable que es un concreto ArrayList puedes crear un ArrayList de la lista inmutable:

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

1725
2018-06-17 04:15



La respuesta simple

En Java 8 o anterior:

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

Esto te dará una List respaldado por la matriz, por lo que no puede cambiar la longitud.
Pero puedes llamar List.setasí que sigue siendo mutable


En Java 9:

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

Esto te dará un inmutable Listpor lo que no se puede cambiar
Que es lo que quieres en la mayoría de los casos donde lo estás prepoblando.


La respuesta más corta

Puedes hacer Arrays.asList incluso más corto con una importación estática:

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

La importación estática:

import static java.util.Arrays.asList;  

Lo que cualquier IDE moderno sugerirá y automáticamente hará por usted.
Por ejemplo, en IntelliJ IDEA, presione Alt+Enter y seleccione Static import method....


Sin embargo, no recomiendo acortar el Java 9 List.of método, porque teniendo solo of se vuelve confuso
List.of ya es lo suficientemente corto y se lee bien.


Utilizando Streams

¿Por qué tiene que ser un List?
Con Java 8 o posterior puedes usar un Stream que 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 desde un Stream a un List:

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

Pero preferiblemente, solo use el Stream sin recogerlo en un List.


Si tu De Verdad específicamente necesito una java.util.ArrayList

(Probablemente no.)
Citar JEP 269 (énfasis mío):

Hay un pequeño conjunto de casos de uso para inicializar una instancia de colección mutable con un conjunto predefinido de valores. Por lo general, es preferible tener esos valores predefinidos en una colección inmutable, y luego inicializar la colección mutable a través de un constructor de copia.


Si quieres ambos rellenar una ArrayList  y agregarlo después (¿por qué?), usar

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

o en Java 9:

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

o usando Stream:

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

Pero, de nuevo, es mejor usar el Stream directamente en lugar de recogerlo en un List.


Programa a interfaces, no a implementaciones

Dijiste que declaraste la lista como un ArrayList en tu código, pero solo deberías hacerlo si estás usando algún miembro de ArrayList eso no está en List.

Lo que probablemente no estés haciendo.

Por lo general, solo debe declarar las variables por la interfaz más general que va a utilizar (p. Iterable, Collection, o List) e inicializarlos con la implementación específica (p. ArrayList, LinkedList o Arrays.asList())

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

Por ejemplo:

// Iterable if you just need iteration, for (String s : strings):
Iterable<String> strings = new ArrayList<>();   

// Collection if you also need .size() or .stream():
Collection<String> strings = new ArrayList<>(); 

// List if you also need .get(index):
List<String> strings = new ArrayList<>();       

// Don't declare a specific list implementation
// unless you're sure you need it:
ArrayList<String> strings = new ArrayList<>();  // You don't need ArrayList

Otro ejemplo sería siempre declarar una variable InputStream a pesar de que generalmente es un FileInputStream o una BufferedInputStream, porque un día pronto tú u otra persona querrán usar algún otro tipo de InputStream.


580
2017-09-09 12:33



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");

100
2017-08-30 04:33



Con Guayaba puedes escribir:

ArrayList<String> places = Lists.newArrayList("Buenos Aires", "Córdoba", "La Plata");

En Guava también hay otros constructores estáticos útiles. Puedes leer sobre ellos aquí.


52
2017-07-29 13:24



Los literales de colección no llegaron a Java 8, pero es posible usar Stream API para inicializar una lista en una sola línea larga:

List<String> places = Stream.of("Buenos Aires", "Córdoba", "La Plata").collect(Collectors.toList());

Si necesita asegurarse de que su List es un ArrayList:

ArrayList<String> places = Stream.of("Buenos Aires", "Córdoba", "La Plata").collect(Collectors.toCollection(ArrayList::new));

32
2018-04-03 23:21



import com.google.common.collect.ImmutableList;

....

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

26
2018-05-12 13:14



Podrías crear un método de fábrica:

public static ArrayList<String> createArrayList(String ... elements) {
  ArrayList<String> list = new ArrayList<String>();
  for (String element : elements) {
    list.add(element);
  }
  return list;
}

....

ArrayList<String> places = createArrayList(
  "São Paulo", "Rio de Janeiro", "Brasília");

Pero no es mucho mejor que tu primera refactorización.

Para una mayor flexibilidad, puede ser genérico:

public static <T> ArrayList<T> createArrayList(T ... elements) {
  ArrayList<T> list = new ArrayList<T>();
  for (T element : elements) {
    list.add(element);
  }
  return list;
}

22
2018-05-04 00:57



Con Java 9, como se sugiere en Propuesta de mejora JDK - 269, esto podría lograrse usando literales de colección no fue -

List<String> list = List.of("A", "B", "C");

Set<String> set = Set.of("A", "B", "C");

También se aplicaría un enfoque similar a Map también -

Map<String, String> map = Map.of("k1", "v1", "k2", "v2", "k3", "v3")

que es similar a Propuesta de Collection Literals según lo indicado por @coobird también. También se aclaró en el documento JEP:


Alternativas

Los cambios de idioma se han considerado varias veces y se han rechazado:

Proyecto de propuesta de monedas, 29 de marzo de 2009 

Proyecto Coin Proposal, 30 de marzo de 2009 

Discusión JEP 186 sobre lambda-dev, enero-marzo de 2014

El idioma   las propuestas se dejaron de lado con preferencia a una propuesta basada en la biblioteca como   resumido en este mensaje.

Lectura encadenada sobre lo mismo ~> ¿Cuál es el punto de los Convenience Factory Methods sobrecargados?


17
2018-02-02 17:36



En Java 9 podemos inicializar fácilmente un ArrayList en una sola línea:

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

o

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

Este nuevo enfoque de Java 9 tiene muchas ventajas sobre los anteriores:

  1. Eficiencia espacial
  2. Inmutabilidad
  3. A salvo de amenazas

Vea esta publicación para más detalles -> ¿Cuál es la diferencia entre List.of y Arrays.asList?


11
2017-10-06 01:16