Гува однострочный для преобразования неизменяемых списков

Я думаю, что должно быть однострочное решение Guava для преобразования неизменяемого списка в другой неизменный список, но я не могу его найти. Предположим, что у нас есть следующие объекты:

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;
    }
};

Преобразование может быть выполнено следующим образом:

Iterable<String> transformedIt = Iterables.transform(input, function);
ImmutableList<String> output = ImmutableList.<String>builder().addAll(transformedIt).build();

или вот так:

List<String> transformedList = Lists.transform(input, function);
ImmutableList<String> output2 = ImmutableList.copyOf(transformedList);

но я думаю, что для такого рода преобразований должен быть где-то оптимизированный по производительности однострочный, без промежуточных объектов, я просто не могу его найти. Где это?

Ответы

Ответ 1

Вы можете просто удалить свой строитель и встроить его, чтобы получить (более длинный) однострочный

ImmutableList<String> output =
    ImmutableList.copyOf(Iterables.transform(input, function));

Это своего рода оптимальное значение, поскольку результат Iterables.transform ленив, поэтому временный список не выделяется. Есть AFAIK некоторые незначительные неэффективности:

  • Выделение FluentIterable
  • Изменение размера массива, используемого для результата

Если вам действительно очень нужна скорость, вы можете сравнить ее и сравнить с чем-то вроде

ArrayList<String> tmp = Lists.newArrayListWithCapacity(input.size());
Iterables.addAll(tmp, Iterables.transform(input, function));
ImmutableList<String> output = ImmutableList.copyOf(tmp);

и в петлю с ручкой.

UPDATE

В то время как первый подход, безусловно, является наиболее читаемым, он берет на себя некоторые распределения для изменения размера массива, а также окончательное сокращение до нужного размера. Со списком длины 1234567 существуют следующие шаги изменения размера:

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

и окончательное сокращение

1574801 → 1234567

ОБНОВЛЕНИЕ 2

Как сказал Луис и Крис, оптимальным решением является

ImmutableList<String> output =
    ImmutableList.copyOf(Lists.transform(input, function));

поскольку он не включает копирование массива. Это работает в результате Lists.transform - это ленивая коллекция, а ImmutableList.copyOf запрашивает свой размер, чтобы выделить массив с соответствующим размером. Обратите внимание, что ни Iterables.transform, ни FluentIterable не эффективны.

Ответ 2

Я думаю, что вы уже написали несколько примеров таких однострочных. Преобразование выполняется с минимальным созданием новых объектов. Действительно, Guava работает лениво: он не перебирает ваш список, создает другие элементы и помещает их в другой список. Он создает ленивый список, который заполняется до тех пор, пока его элементы не нужны, например. в то время как вы повторяете новый список. Я думаю, что java 8 с закрытием не будет слишком быстрым для этого варианта использования, потому что он выполнит аналогичный байт-код, но синтаксис будет короче.