下記のようにリストのリストを作成して、最後に一括して要素(例:999)を追加する処理です
java
1 public static void main(String args[]) { 2 ArrayList<ArrayList<Integer>> data = new ArrayList<ArrayList<Integer>>(); 3 4 for (int i=0; i<=3; i++) { 5 ArrayList<Integer> element = new ArrayList<Integer>(); 6 element.add(i*1); 7 element.add(i*2); 8 element.add(i*3); 9 10 data.add(element); 11 } 12 13 data.forEach(e->e.add(999)); 14 System.out.println(data); 15 }
結果
[[0, 0, 0, 999], [1, 2, 3, 999], [2, 4, 6, 999], [3, 6, 9, 999]]
ただ、いろいろ調べていく過程で
java
1data.stream().map(e->e.add(999));
のように、stream apiを使う書き方もありました。
ただ、こちらの書き方では、ArrayList<ArrayList<Integer>>
へキャストすることができなかったので挫折しました。
【質問】
① stream api と foreachの違いはなんでしょうか?foreachではdata
が書き換わってしまうという事ぐらいでしょうか?(stream api は新しいcollection
に対しての操作という認識)
② 上記のプログラムのように foreach
で処理することに問題はあるでしょうか?
③ stream apiを使うとしたら、どのように元のListにキャストするのでしょうか?
よろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
① stream api と foreachの違いはなんでしょうか?foreachではdataが書き換わってしまうという事ぐらいでしょうか?(stream api は新しいcollectionに対しての操作という認識)
凄く雑な言い方をするなら、
forEachメソッドは、拡張for文を使って記述するコレクションの要素への繰り返し操作を、
「for文の中身だけ」記述するやり方です。
for文の中身を記述するための型としてConsumerが定義されています。
一方Stream APIは、ベルトコンベアーか何かを使った流れ作業のベースを作り、
作業に必要なものを組み合わせて工場を作るようなイメージです。
forEachは各要素に逐次処理を行うだけですが、streamの場合はフィルタを掛けたり要素を変換したりといったことが比較的直観的に書けます。
両者ともに元のdataを書き換えることはありません。ただし、今回のように中身がArrayListのような可変オブジェクトである場合は、それが変更されることはありえます。
② 上記のプログラムのように foreachで処理することに問題はあるでしょうか?
全く問題ありません。先述の通り拡張for文でやってることと何ら変わりありません。
③ stream apiを使うとしたら、どのように元のListにキャストするのでしょうか?
「キャスト」という語を使うのは適切ではないです。先述の通りStreamはベルトコンベアーでの作業をしているような状態で、Streamそれ自体はコレクションではありません。最終的に適切な「終端操作」を行うことで結果を出します。
これの場合は、collectというメソッドを使ってコレクションにまとめます。
投稿2020/02/06 19:47
総合スコア20669
0
ベストアンサー
他の回答者とほぼ同じ内容ですがポストします。
準備
data
の初期化部分です。誰が書いてもあまり変わりません。for(...)
ループでも同じ事ができます。
Java
1List<List<Integer>> data = null, data2 = null; 2Supplier<List<List<Integer>>> supplier = 3 () -> IntStream.rangeClosed(0,3) 4 .mapToObj(k -> Arrays.asList(1*k,2*k,3*k)) 5 .map(ArrayList::new) // to mutable list 6 .collect(Collectors.toList()); 7data = supplier.get();
forEach
① stream api と foreachの違いはなんでしょうか?
forEach()はCollectionとStreamの両方にあります。どちらも引数にConsumerを取ります。違いはThe Difference Between Collection.stream().forEach() and Collection.forEach()に書かれているとおり、Collectionが内部的にIteratorを使用するのに対してStreamはListを順にトラバースします。Iteratorを書き換えると処理順が変わります。この例でその必要はなさそうです。
Java
1data.forEach(e->e.add(999)); 2data.stream().forEach(e->e.add(999));
foreachではdataが書き換わってしまうという事ぐらいでしょうか?(stream api は新しいcollectionに対しての操作という認識)
data.stream().forEach()は元のデータを書き換える副作用があります(新しい出力を作らず終端処理で自己更新する)。(*swordoneさんのdata.forEach()は副作用ではないという意見に同意。以後、Stream処理が入力を汚すという意味で「副作用」を使います)
map/peek
さらに副作用のある例を2つ示します。map()は戻り値を返す必要があります。peek()は戻り値なしです。
Java
1data.stream().map(e -> {e.add(999);return e;}).collect(Collectors.toList()); 2data.stream().peek(e -> e.add(999)).collect(Collectors.toList());
③ stream apiを使うとしたら、どのように元のListにキャストするのでしょうか?
すでに(List<Integer>)のStreamになっているのでキャストできません。.collect(Collectors.toList())
で、新しく外側のListを作ってもとの中身を格納します。
すべてを作り直す
中身をすべてコピーして新しく作るやり方です。入力はそのまま残り、副作用はありません。この方法だとdata
を作る時にArrayList::new
が不要になります。
Java
1data.stream() 2 .map(e -> Stream.concat(e.stream(),Stream.of(999)).collect(Collectors.toList())) 3 .collect(Collectors.toList());
② 上記のプログラムのように foreachで処理することに問題はあるでしょうか?
- Stream/Collectionは、どちらも副作用がある
- Streamは、filter/mapなどのメソッドチェーンが使えるので、データを加工するならStreamを使う
- Collectionは、Iteratorを書き換えて、処理の順番を変えることができる
Streamの利用
Streamhは並行処理で利用することがあり、副作用が起きないようにimmutableなコレクションを扱うほうがよい。また、immutableにしておけば、誤ってコレクションを更新しても例外が発生する。
投稿2020/02/05 14:13
編集2020/02/07 04:40総合スコア1090
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
③ stream apiを使うとしたら、どのように元のListにキャストするのでしょうか?
dataの型を ArrayList<ArrayList<Integer>> にすることに拘るなら。
Java
1import java.util.ArrayList; 2import java.util.List; 3import java.util.stream.Collectors; 4import java.util.stream.IntStream; 5 6class Main { 7 public static void main(String args[]) { 8 ArrayList<ArrayList<Integer>> data = IntStream.rangeClosed(1, 3) 9 .mapToObj(i -> 10 new ArrayList<>(List.of(i*1, i*2, i*3, 999)) 11 ) 12 .collect(Collectors.toCollection(ArrayList::new)) 13 ; 14 15 System.out.println(data); 16 } 17}
① stream api と foreachの違いはなんでしょうか?foreachではdataが書き換わってしまうという事ぐらいでしょうか?(stream api は新しいcollectionに対しての操作という認識)
② 上記のプログラムのように foreachで処理することに問題はあるでしょうか?
修正: 論拠に乏しい記述であった為、修正します。詳細はコメントをご覧下さい。
Stream API にせよ List#forEach にせよ、副作用のある処理を書くには向かないように思います。
もちろん可能ではあるのですが、コードを読むときに少し引っ掛かりがあります。
Stream API は、副作用のある処理を書くのには向きません。
純朴なfor文で書いた方が素直に読めるのならそれが正解でしょう。
Java
1import java.util.ArrayList; 2import java.util.List; 3 4class Main { 5 public static void main(String args[]) { 6 ArrayList<ArrayList<Integer>> data = new ArrayList<>(); 7 for(int i = 1; i <= 3; ++i) { 8 data.add( 9 new ArrayList<>(List.of(i*1, i*2, i*3, 999)) 10 ); 11 } 12 13 System.out.println(data); 14 } 15}
あるいは
Java
1for(var row: data) { 2 row.add(999); 3} 4 5System.out.println(data);
投稿2020/02/05 13:17
編集2020/02/06 23:28総合スコア35668
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/02/06 19:42
2020/02/06 23:24
2020/02/07 04:16
0
③ stream apiを使うとしたら、どのように元のListにキャストするのでしょうか?
ひとつの例です。
java
1 public static void main(String args[]) { 2 final List<ArrayList<Integer>> data = 3 IntStream.rangeClosed(0, 3) 4 .mapToObj(i -> 5 IntStream.rangeClosed(1, 3) 6 .mapToObj(num -> Integer.valueOf(i * num)) 7 .collect(Collectors.toList() 8 ) 9 ) 10 .map(ArrayList::new) 11 .collect(Collectors.toList()); 12 System.out.println(data); 13 14 data.forEach(it -> it.add(999)); 15 System.out.println(data); 16 }
① stream api と foreachの違いはなんでしょうか?foreachではdataが書き換わってしまうという事ぐらいでしょうか?(stream api は新しいcollectionに対しての操作という認識)
data.forEach(it -> it.add(999));
は、data.stream().forEach(it -> it.add(999));
の短縮形|省略形だと捉えてみてはどうでしょうか?forEach()メソッドもStreamAPIの一部だ、と。ただ、forEach()メソッドの中身をみると実際の実装は違うようですけれど。
② 上記のプログラムのように foreachで処理することに問題はあるでしょうか?
たとえば、StreamAPIを使うとparallel()を挟むだけで簡単に並列処理ができたりしますね(この程度の処理ではむしろ効率悪くなりますけど)。
java
1 final List<ArrayList<Integer>> data = 2 IntStream.rangeClosed(0, 3) 3 .parallel() 4 .mapToObj(i ->
余談ですが、StreamAPIを使い始めて、collectとか使わなくちゃいけなくて、なんかめんどうくさいなぁ、とおもいはじめてKotlinをみると、ラクそうにみえてくるとおもいます。
kotlin
1 fun main(args: Array<String>) { 2 val data: List<MutableList<Int>> = 3 (0..3).map { i -> 4 (1..3).map { num -> num * i }.toMutableList() 5 } 6 println(data) 7 8 data.forEach { it.add(999) } 9 println(data) 10 }
投稿2020/02/05 12:45
編集2020/02/05 13:26総合スコア4061
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。