I wanted to create a list of options for testing purposes. At first, I did this:
ArrayList places = new ArrayList();
places.add(\
In Java 9 or later, after List.of()
was added:
List strings = List.of("foo", "bar", "baz");
With Java 10 or later, this can be shortened with the var
keyword.
var strings = List.of("foo", "bar", "baz");
This will give you an immutable List
, so it cannot be changed.
Which is what you want in most cases where you're prepopulating it.
List strings = Arrays.asList("foo", "bar", "baz");
This 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.
You can make Arrays.asList
even shorter with a static import:
List strings = asList("foo", "bar", "baz");
The static import:
import static java.util.Arrays.asList;
Which any modern IDE will suggest and automatically do for you.
For example in IntelliJ IDEA you press Alt+Enter
and select Static import method...
.
However, i don't recommend shortening the List.of
method to of
, because that becomes confusing.
List.of
is already short enough and reads well.
Stream
sWhy does it have to be a List
?
With Java 8 or later you can use a Stream
which is more flexible:
Stream strings = Stream.of("foo", "bar", "baz");
You can concatenate Stream
s:
Stream 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;
List strings = Stream.of("foo", "bar", "baz").collect(toList());
But preferably, just use the Stream
without collecting it to a List
.
java.util.ArrayList
(You probably don't.)
To quote JEP 269 (emphasis mine):
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.
If you want to both prepopulate an ArrayList
and add to it afterwards (why?), use
ArrayList strings = new ArrayList<>(List.of("foo", "bar"));
strings.add("baz");
or in Java 8 or earlier:
ArrayList strings = new ArrayList<>(asList("foo", "bar"));
strings.add("baz");
or using Stream
:
import static java.util.stream.Collectors.toCollection;
ArrayList strings = Stream.of("foo", "bar")
.collect(toCollection(ArrayList::new));
strings.add("baz");
But again, it's better to just use the Stream
directly instead of collecting it to a List
.
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 strings) {
for (String s : strings) { ... }
}
// Collection if you also need .size(), .isEmpty(), or .stream():
void method(Collection strings) {
if (!strings.isEmpty()) { strings.stream()... }
}
// List if you also need .get(index):
void method(List strings) {
strings.get(...)
}
// Don't declare a specific list implementation
// unless you're sure you need it:
void method(ArrayList 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
.