Inicialización de un ArrayList en una línea
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?
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 places
es inmutable (intentar cambiarlo provocará que UnsupportedOperationException
se genere una excepción).
Para hacer una lista mutable que sea concreta, ArrayList
puede crear una ArrayList
a 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;
En realidad, probablemente la "mejor" forma de inicializar ArrayList
es el método que escribiste, ya que no es necesario crear uno nuevo List
de 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 list
instancia.
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 ArrayList
tiene 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 List
en lugar de un ArrayList
, y además, aún no está disponible, si es que alguna vez lo estará.
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 null
elementos.
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 null
elementos.
*
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.asList
aú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.of
método como just of
, porque es confuso.
*
Por ejemplo, en IntelliJ IDEA presionas Alt+Enter
y seleccionasStatic import method...
Usando Stream
s
¿Por qué tiene que ser un List
?
Con Java 8 o posterior puedes usar uno Stream
que es más flexible:
Stream<String> strings = Stream.of("foo", "bar", "baz");
Puedes concatenar Stream
s:
Stream<String> strings = Stream.concat(Stream.of("foo", "bar"),
Stream.of("baz", "qux"));
O puedes ir de a Stream
a 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 Stream
sin 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 Stream
directamente 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 ArrayList
en tu código, pero solo debes hacerlo si estás usando algún miembro que ArrayList
no 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
, Collection
o List
) e inicializarlas con la implementación específica (por ejemplo ArrayList
, LinkedList
o 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 ArrayList
a 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 InputStream
aunque generalmente sea a FileInputStream
o a BufferedInputStream
, porque algún día, pronto, usted o alguien más querrá usar algún otro tipo de InputStream
.
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");