状態を持つラムダ式が非推奨なのはなぜでしょうか
解決済
回答 2
投稿
- 評価
- クリップ 0
- VIEW 1,598

退会済みユーザー
いつもお世話になります。
ある書籍に『(推奨しませんが)状態を持つラムダ式を作りたい場合』という例で、
フィールドにラムダ式からアクセスする方法が載っていました。
さて、現在以下のようにオブジェクトのリストをArrayList<ArrayList<String>>になおす
処理を書いたのですが、これもあまりよろしくない作法の記述になるのでしょうか。
(書籍にあるようなラムダ式内からフィールド変数にアクセスしているので。)
private int cnt; //インスタンスフィールド。ラムダ式内でインクリメントさせる。
/**
* Functionオブジェクト.
* PersonをArrayListへ変換する
* @return
*/
private Function<Person, ArrayList<String>> convertToArray() {
return p -> {
ArrayList<String> list = new ArrayList<>();
list.add(String.valueOf(cnt)); //←ここ!連番を入れたいので、インスタンスフィードを用いる
list.add(p.getName());
list.add(String.valueOf(p.getAge()));
cnt++; //フィールドを変更 ←これがまずい?
return list;
};
}
//Fuctionを用いた処理
public void hoge() {
Person p1 = new Person("taro", 5);
Person p2 = new Person("jiro", 10);
Person p3 = new Person("saburo", 15);
Person p4 = new Person("siro", 20);
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
//実際にリストのstream処理で上記のFunctionを用いて変換する
list.stream().map(convertToArray()).forEach(System.out::println);
}
結果
[0, taro, 5]
[1, jiro, 10]
[2, saburo, 15]
[3, siro, 20]
期待通りの動きにはなるのですが、ただ推奨されない理由が気になってしまい・・。
ご教示よろしくお願いします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+7
Streamは並列で使われることも想定されています。
つまり、Listに収められている要素に対して順番に処理を行うのではなく、
適宜切り分けて同時進行するという状態です。
その場合、このコードの場合ではListの真ん中あたりに位置していたはずの要素に2番などの早い番号が付く可能性があります。
しかし、Streamの仕様として順次処理と並列処理で結果が変わるべきではありません。
そのため、Streamに使われることが想定されたラムダ式では、状態を持つべきではないのだと思われます。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+2
Java公式のstreamパッケージリファレンスに言及があります(一部省略;強調あり):
ステートレス動作
ストリーム・パイプラインの結果が非決定論的または不正となる可能性がある のは、ストリーム操作の動作パラメータがステートフルの場合です。ステートフルなラムダ(または対応する関数型インタフェースを実装したその他のオブジェクト)とは、ストリーム・パイプラインの実行中に変化する可能性のある状態に結果が依存するようなラムダのことです。(中略)ここで、マッピング操作が並列実行されると、スレッドのスケジューリングの違いにより、同じ入力に対する結果が実行のたびに変わる可能性があります。一方、ステートレスなラムダ式では、結果は常に同じになります。
また、動作パラメータから可変状態へのアクセスを試みることは、安全性やパフォーマンスの点から良くない 選択肢と言えます。その状態へのアクセスの同期を取らなかった場合、データ競合が発生するためにコードが中断してしまいますが、その状態へのアクセスの同期を取った場合、得ようとしている並列性のメリットが競合によって薄れてしまう危険性があります。最良のアプローチは、ストリーム操作のステートフル動作パラメータを一切使用しないことです。通常は、ストリーム・パイプラインを再構成してステートフルになるのを避ける方法があります。
.
副作用
ストリーム操作の動作パラメータでの副作用は一般にお薦めできません。そのような 副作用はしばしば、ステートレス要件への無意識の違反や、スレッドの安全性を脅かすその他の危険につながる可能性がある からです。動作パラメータが副作用を持つ場合は、特に明記されていない限り、他のスレッドからのそうした副作用の可視性に関する保証は一切なく、同じストリーム・パイプライン内での同じ要素に対するさまざまな操作が同一スレッド内で実行される保証もありません。さらに、そうした副作用の順序付けは、驚くべきものになる可能性があります。ストリーム・ソースの検出順序と矛盾しない結果を生成するようにパイプラインが制約されている場合でも、マッパー関数が個々の要素に適用される順序や、任意の動作パラメータがどのスレッド内で特定の要素に対して実行されるか、に関する保証は一切ありません。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 89.99%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2017/06/09 20:40
重ね重ね申し訳ないのですが、上記のようなコードにて、推奨されない方法(上記のようなフィールド変数を用いたやり方)以外の方法で同様の結果になるラムダ式の使用方法はあるのでしょうか。
(オブジェクト→リスト変換のFunction処理中に、リストの先頭要素をインクリメントさせていきたい。)
2017/06/09 22:31 編集
誰かが言っていたStreamのzipなどの機能がないため、番号と要素を新たに対応付けるという使い方が出来ません。
素直にforを使うのが無難です。
2017/06/10 09:02
そうなのですね。たびたびありがとうございました。