The simple answer
Java 9 or later:
List<String> strings = List.of("foo", "bar", "baz");
List.of(...)
will give you an immutable List
, so it cannot be changed.
Which is what you want in most cases where you're prepopulating it.
This does not allow null
elements.
Java 8 or earlier:
List<String> strings = Arrays.asList("foo", "bar", "baz");
Arrays.asList(...)
will give you a List
* backed by an array, so it cannot change length.
But you can call List.set(...)
, so it's still mutable.
This does allow null
elements.
*
Implementation detail: It's a private nested class inside java.util.Arrays
, named ArrayList
,
which is a different class from java.util.ArrayList
, even though their simple names are the same.
Static import
You can make Java 8 Arrays.asList
even shorter with a static import:
import static java.util.Arrays.asList;
...
List<String> strings = asList("foo", "bar", "baz");
Any modern IDE* will suggest and do this for you.
I don't recommend statically importing the List.of
method as just of
, because it's confusing.
*
For example, in IntelliJ IDEA you press Alt+Enter
and select Static import method...
Using Stream
s
Why does it have to be a List
?
With Java 8 or later you can use a Stream
which is more flexible:
Stream<String> strings = Stream.of("foo", "bar", "baz");
You can concatenate Stream
s:
Stream<String> strings = Stream.concat(Stream.of("foo", "bar"),
Stream.of("baz", "qux"));
Or you can go from a Stream
to 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
But preferably, just use the Stream
without collecting it to a List
.
If you specifically need a java.util.ArrayList
*
If you want to both prepopulate an ArrayList
and add to it afterwards, use
List<String> strings = new ArrayList<>(List.of("foo", "bar"));
or in Java 8 or earlier:
List<String> strings = new ArrayList<>(asList("foo", "bar"));
or using Stream
:
import static java.util.stream.Collectors.toCollection;
List<String> strings = Stream.of("foo", "bar")
.collect(toCollection(ArrayList::new));
Then you can add to it after construction:
strings.add("baz");
But again, it's better to just use the Stream
directly instead of collecting it to a List
.
*You probably don't need specifically an ArrayList
. To quote JEP 269:
There is a small set of use cases for initializing a mutable collection instance with a predefined set of values. It's usually preferable to have those predefined values be in an immutable collection, and then to initialize the mutable collection via a copy constructor.
(emphasis mine)
Program to interfaces, not to implementations
You said you've declared the list as an ArrayList
in your code, but you should only do that if you're using some member of ArrayList
that's not in List
.
Which you are most likely not doing.
Usually you should just declare variables by the most general interface that you are going to use (e.g. Iterable
, Collection
, or List
), and initialize them with the specific implementation (e.g. ArrayList
, LinkedList
or Arrays.asList()
).
Otherwise you're limiting your code to that specific type, and it'll be harder to change when you want to.
For example, if you're passing an ArrayList
to 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
}
Another example would be always declaring variable an InputStream
even though it is usually a FileInputStream
or a BufferedInputStream
, because one day soon you or somebody else will want to use some other kind of InputStream
.
ArrasyList<String> places = ["Buenos Aires", "Córdoba", "La Plata"]
– ErrantryList<Integer> = {1, 2, 3, 4, 5}
in Java. Create and init a mutable array in one line, as I know Python and Swift could do that. – Hylomorphism