質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

1254閲覧

状態を持つラムダ式が非推奨なのはなぜでしょうか

退会済みユーザー

退会済みユーザー

総合スコア0

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2017/06/08 15:08

いつもお世話になります。

ある書籍に『**(推奨しませんが)**状態を持つラムダ式を作りたい場合』という例で、
フィールドにラムダ式からアクセスする方法が載っていました。

さて、現在以下のようにオブジェクトのリストをArrayList<ArrayList<String>>になおす
処理を書いたのですが、これもあまりよろしくない作法の記述になるのでしょうか。
(書籍にあるようなラムダ式内からフィールド変数にアクセスしているので。)

java

1 2 private int cnt; //インスタンスフィールド。ラムダ式内でインクリメントさせる。 3 4 /** 5 * Functionオブジェクト. 6 * PersonをArrayListへ変換する 7 * @return 8 */ 9 private Function<Person, ArrayList<String>> convertToArray() { 10 return p -> { 11 ArrayList<String> list = new ArrayList<>(); 12 list.add(String.valueOf(cnt)); //←ここ!連番を入れたいので、インスタンスフィードを用いる 13 list.add(p.getName()); 14 list.add(String.valueOf(p.getAge())); 15 cnt++; //フィールドを変更 ←これがまずい? 16 return list; 17 }; 18 } 19 20 21 //Fuctionを用いた処理 22 public void hoge() { 23 24 Person p1 = new Person("taro", 5); 25 Person p2 = new Person("jiro", 10); 26 Person p3 = new Person("saburo", 15); 27 Person p4 = new Person("siro", 20); 28 29 List<Person> list = new ArrayList<>(); 30 list.add(p1); 31 list.add(p2); 32 list.add(p3); 33 list.add(p4); 34 35 //実際にリストのstream処理で上記のFunctionを用いて変換する 36 list.stream().map(convertToArray()).forEach(System.out::println); 37 38 } 39 40

結果

[0, taro, 5] [1, jiro, 10] [2, saburo, 15] [3, siro, 20]

期待通りの動きにはなるのですが、ただ推奨されない理由が気になってしまい・・。
ご教示よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

Streamは並列で使われることも想定されています。
つまり、Listに収められている要素に対して順番に処理を行うのではなく、
適宜切り分けて同時進行するという状態です。
その場合、このコードの場合ではListの真ん中あたりに位置していたはずの要素に2番などの早い番号が付く可能性があります。
しかし、Streamの仕様として順次処理と並列処理で結果が変わるべきではありません。
そのため、Streamに使われることが想定されたラムダ式では、状態を持つべきではないのだと思われます。

投稿2017/06/08 15:48

swordone

総合スコア20649

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/06/09 11:40

ありがとうございます。勉強になりました。 重ね重ね申し訳ないのですが、上記のようなコードにて、推奨されない方法(上記のようなフィールド変数を用いたやり方)以外の方法で同様の結果になるラムダ式の使用方法はあるのでしょうか。 (オブジェクト→リスト変換のFunction処理中に、リストの先頭要素をインクリメントさせていきたい。)
swordone

2017/06/09 14:22 編集

多分ないです。 誰かが言っていたStreamのzipなどの機能がないため、番号と要素を新たに対応付けるという使い方が出来ません。 素直にforを使うのが無難です。
退会済みユーザー

退会済みユーザー

2017/06/10 00:02

回答ありがとうございます。 そうなのですね。たびたびありがとうございました。
guest

0

Java公式のstreamパッケージリファレンスに言及があります(一部省略;強調あり):

ステートレス動作
ストリーム・パイプラインの結果が非決定論的または不正となる可能性がある のは、ストリーム操作の動作パラメータがステートフルの場合です。ステートフルなラムダ(または対応する関数型インタフェースを実装したその他のオブジェクト)とは、ストリーム・パイプラインの実行中に変化する可能性のある状態に結果が依存するようなラムダのことです。(中略)

ここで、マッピング操作が並列実行されると、スレッドのスケジューリングの違いにより、同じ入力に対する結果が実行のたびに変わる可能性があります。一方、ステートレスなラムダ式では、結果は常に同じになります。

また、動作パラメータから可変状態へのアクセスを試みることは、安全性やパフォーマンスの点から良くない 選択肢と言えます。その状態へのアクセスの同期を取らなかった場合、データ競合が発生するためにコードが中断してしまいますが、その状態へのアクセスの同期を取った場合、得ようとしている並列性のメリットが競合によって薄れてしまう危険性があります。最良のアプローチは、ストリーム操作のステートフル動作パラメータを一切使用しないことです。通常は、ストリーム・パイプラインを再構成してステートフルになるのを避ける方法があります。

.

副作用
ストリーム操作の動作パラメータでの副作用は一般にお薦めできません。そのような 副作用はしばしば、ステートレス要件への無意識の違反や、スレッドの安全性を脅かすその他の危険につながる可能性がある からです。

動作パラメータが副作用を持つ場合は、特に明記されていない限り、他のスレッドからのそうした副作用の可視性に関する保証は一切なく、同じストリーム・パイプライン内での同じ要素に対するさまざまな操作が同一スレッド内で実行される保証もありません。さらに、そうした副作用の順序付けは、驚くべきものになる可能性があります。ストリーム・ソースの検出順序と矛盾しない結果を生成するようにパイプラインが制約されている場合でも、マッパー関数が個々の要素に適用される順序や、任意の動作パラメータがどのスレッド内で特定の要素に対して実行されるか、に関する保証は一切ありません。

投稿2017/06/10 06:18

yohhoy

総合スコア6189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/06/11 06:57

ありがとうございます。 原典を見ることをしていませんでした。 今後は一次情報も確認するようにします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問