1. 概要

Javaコレクションをあるタイプから別のタイプに変換することは、一般的なプログラミングタスクです。 このチュートリアルでは、任意のタイプのコレクションArrayListに変換します。

チュートリアル全体を通して、Fooオブジェクトのコレクションがすでにあると想定します。 そこから、さまざまなアプローチを使用してArrayListを作成します。

2. 例の定義

ただし、続行する前に、入力と出力をモデル化しましょう。

ソースは任意のタイプのコレクションである可能性があるため、Collectionインターフェイスを使用して宣言します。

Collection<Foo> srcCollection;

同じ要素タイプでArrayListを生成する必要があります。

ArrayList<Foo> newList;

3. ArrayListコンストラクターの使用

コレクションを新しいコレクションにコピーする最も簡単な方法は、そのコンストラクターを使用することです。

以前のArrayListガイドでは、ArrayListコンストラクターがコレクションパラメーターを受け入れることができることを学びました。

ArrayList<Foo> newList = new ArrayList<>(srcCollection);
  • 新しいArrayListには、ソースコレクションのFoo要素の浅いコピーが含まれています。
  • 順序は、ソースコレクションの順序と同じです。

コンストラクターは単純であるため、ほとんどのシナリオで優れたオプションになります。

4. StreamsAPIの使用

ここで、 Streams APIを利用して、既存のコレクションからArrayListを作成しましょう

ArrayList<Foo> newList = srcCollection.stream().collect(toCollection(ArrayList::new));

このスニペットでは:

  • ソースコレクションからストリームを取得し、 collect()演算子を適用して、リストを作成します。
  • ArrayList :: new を指定して、必要なリストタイプを取得します
  • このコードは、浅いコピーも生成します。

正確なListタイプを気にしない場合は、次のように簡略化できます。

List<Foo> newList = srcCollection.stream().collect(toList());

toCollection()および toList()は、Collectorsから静的にインポートされることに注意してください。 詳細については、Java8のコレクターに関するガイドを参照してください。

5. ディープコピー

「浅いコピー」について言及する前に。 つまり、新しいリストの要素は、ソースコレクションにまだ存在するとまったく同じFooインスタンスであるということです。 そのため、参照によりFoonewListにコピーしました。

いずれかのコレクションのFooインスタンスの内容を変更すると、の変更は両方のコレクションに反映されます。 したがって、一方のコレクションの要素を、もう一方のコレクションを変更せずに変更する場合は、「ディープコピー」を実行する必要があります。

Foo をディープコピーするために、各要素に対して完全に新しいFooインスタンスを作成します。 したがって、すべてのFooフィールドを新しいインスタンスにコピーする必要があります。

Foo クラスを定義して、それ自体をディープコピーする方法を認識できるようにします。

public class Foo {

    private int id;
    private String name;
    private Foo parent;

    public Foo(int id, String name, Foo parent) {
        this.id = id;
        this.name = name;
        this.parent = parent;
    }

    public Foo deepCopy() {
        return new Foo(
          this.id, this.name, this.parent != null ? this.parent.deepCopy() : null);
    }
}

ここでは、フィールドidnameintStringであることがわかります。 これらのデータ型は値によってコピーされます。 したがって、両方を簡単に割り当てることができます。

parent フィールドは、クラスである別のFooです。 Foo が変更された場合、その参照を共有するコードはすべて、これらの変更の影響を受けます。 親フィールドをディープコピーする必要があります。

これで、ArrayList変換に戻ることができます。 ディープコピーをフローに挿入するには、マップ演算子が必要です。

ArrayList<Foo> newList = srcCollection.stream()
  .map(foo -> foo.deepCopy())
  .collect(toCollection(ArrayList::new));

どちらかのコレクションの内容を、もう一方に影響を与えることなく変更できます。

ディープコピーは、要素の数とデータの深さによっては、時間のかかるプロセスになる可能性があります。 ここで並列ストリームを使用すると、必要に応じてパフォーマンスが向上する場合があります。

6. リストの順序を制御する

デフォルトでは、ストリームは、ソースコレクションで検出されたのと同じ順序で要素をArrayListに配信します。

その順序を変更したい場合、sorted()演算子をストリームに適用できます。 Foo オブジェクトを名前で並べ替えるには:

ArrayList<Foo> newList = srcCollection.stream()
  .sorted(Comparator.comparing(Foo::getName))
  .collect(toCollection(ArrayList::new));

ストリームの順序付けの詳細については、この以前のチュートリアルを参照してください。

7. 結論

ArrayList コンストラクターは、Collectionのコンテンツを新しいArrayListに取り込む効果的な方法です。

ただし、結果のリストを微調整する必要がある場合は、StreamsAPIがプロセスを変更するための強力な方法を提供します。

この記事で使用されているコードは、GitHub全体を見つけることができます。