web-dev-qa-db-ja.com

不変リストを変換するためのGuavaワンライナー

不変リストを別の不変リストに変換するためのワンライナー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);

しかし、この種の変換には、パフォーマンスが最適化されたワンライナーがどこかにあるはずだと思います。中間オブジェクトがなければ、それを見つけることができません。それはどこにある?

20
WannaKnow

ビルダーを削除してインライン化するだけで、(少し長い)ワンライナーを取得できます

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

そして手巻きループに。

更新

最初のアプローチは確かに最も読みやすいアプローチですが、配列のサイズ変更と最終的なサイズへの縮小のためにいくつかの割り当てが発生します。長さ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.transformFluentIterableもそれほど効率的ではないことに注意してください。

24
maaartinus

あなたはすでにそのようなワンライナーのいくつかの例を書いていると思います。変換は、新しいオブジェクトの最小限の作成で行われます。実際、Guavaは怠惰な方法で機能します。リストを反復処理したり、他の要素を作成したり、別のリストに配置したりすることはありません。それは、その要素が必要とされる限り満たされる怠惰なリストを作成します。新しいリストを繰り返し処理している間。 Java 8クロージャを使用すると、同様のバイトコードが実行されますが、構文は短くなるため、このユースケースではそれほど高速ではないと思います。

1
AlexR

input.stream().collect(toImmutableList())が必要なものだと思います。 doc を参照してください

1