I think there must be a one-liner Guava solution for transforming an immutable list into another immutable list, but I can't find it. Suppose we have the following objects:
ImmutableList<String> input = ImmutableList.of("a", "b", "c");
Function<String, String> function = new Function<String, String>() {
@Override
public String apply(String input) {
return input + input;
}
};
The transformation can be achieved like this:
Iterable<String> transformedIt = Iterables.transform(input, function);
ImmutableList<String> output = ImmutableList.<String>builder().addAll(transformedIt).build();
or like this:
List<String> transformedList = Lists.transform(input, function);
ImmutableList<String> output2 = ImmutableList.copyOf(transformedList);
but I think that there must be somewhere a performance-optimized one-liner for this kind of transformation, without intermediate objects, I just can't find it. Where is it?
You can simply remove your builder and inline it to get a (bit longer) one-liner
ImmutableList<String> output =
ImmutableList.copyOf(Iterables.transform(input, function));
This is sort of optimal as the result of Iterables.transform
is lazy, so no temporary list gets allocated. There are AFAIK some minor inefficiencies:
FluentIterable
If you really care a lot about the speed, you could benchmark it and compare to something like
ArrayList<String> tmp = Lists.newArrayListWithCapacity(input.size());
Iterables.addAll(tmp, Iterables.transform(input, function));
ImmutableList<String> output = ImmutableList.copyOf(tmp);
and to a handrolled loop.
While the first approach is surely the most readable one, it incurs some allocations for array resizing and also the final shrinking to desired size. With a list of length 1234567, there are the following resizing steps:
4 -> 7 -> 11 -> 17 -> 26 -> 40 -> 61 -> 92 -> 139 -> 209 -> 314 -> 472 -> 709 -> 1064 -> 1597 -> 2396 -> 3595 -> 5393 -> 8090 -> 12136 -> 18205 -> 27308 -> 40963 -> 61445 -> 92168 -> 138253 -> 207380 -> 311071 -> 466607 -> 699911 -> 1049867 -> 1574801
and the final shrinking
1574801 -> 1234567
As Louis and Chris said, the optimal solution is
ImmutableList<String> output =
ImmutableList.copyOf(Lists.transform(input, function));
as it includes no array copying. This works as the result of Lists.transform
is a lazy collection and ImmutableList.copyOf
queries its size in order to allocate a properly sized array. Note that neither Iterables.transform
nor FluentIterable
are that efficient.