用于转换不可变列表的番石榴单行更新更新 2

2022-09-02 03:56:53

我认为必须有一个单行的番石榴解决方案,用于将一个不可变列表转换为另一个不可变列表,但我找不到它。假设我们有以下对象:

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

这是一种最佳结果,因为结果是懒惰,所以没有分配临时列表。AFAIK存在一些小的低效率:Iterables.transform

  • 分配FluentIterable
  • 调整用于结果的数组的大小

如果你真的非常关心速度,你可以对它进行基准测试,并与类似的东西进行比较。

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

并转到手动滚动的循环。

更新

虽然第一种方法肯定是可读性最强的方法,但它需要为数组大小调整和最终缩小到所需大小进行一些分配。通过长度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.transformImmutableList.copyOfIterables.transformFluentIterable


答案 2

我想你已经写了几个这样的单行的例子。转换是通过最少创建新对象来完成的。事实上,番石榴以懒惰的方式工作:它不会迭代你的列表,创建其他元素并将其放入另一个列表。它创建懒惰列表,该列表根据需要填充其元素,例如,当您迭代新列表时。我认为对于这个用例来说,带有闭包的java 8不会太快,因为它将执行类似的字节代码,但语法会更短。


推荐