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?
No, you cannot make the elements of an array immutable. But the unmodifiableList() method of the java. util. Collections class accepts an object of the List interface (object of implementing its class) and returns an unmodifiable form of the given object.
Immutable List Static Factory Methods. The List. of static factory methods provide a convenient way to create immutable lists. A list is an ordered collection, where duplicate elements are typically allowed.
A mutable collection is not a subtype of an immutable collection.
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.
I think that you have already written several examples of such one-liners. The transformation is done with minimal creation of new objects. Indeed Guava works in lazy manner: it does not iterate over your list, creates other elements and put it to another list. It creates lazy list that is filled as far as its elements are needed, e.g. while you are iterating over the new list. I think that java 8 with closures will not be too much faster for this use case because it will execute similar byte code but the syntax will be shorter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With