ホームページ  >  記事  >  Java  >  Java 8の新機能、ラムダ式の使い方(使用例)

Java 8の新機能、ラムダ式の使い方(使用例)

高洛峰
高洛峰オリジナル
2017-01-23 15:12:301459ブラウズ

ラムダが Java にクロージャの概念をもたらすのを長い間待っていましたが、コレクションでラムダを使用しない場合、多くの価値を失います。既存のインターフェイスをラムダ スタイルに移行するという問題は、デフォルトのメソッドによって解決されました。この記事では、Java コレクションのバルク データ操作 (バルク オペレーション) を深く分析し、ラムダの最も強力な役割の謎を解明します。

1. JSR335 について

JSR は Java Specific Requests の略で、Java のコードを書きやすくすることを目的とした Java 8 バージョンの主な改良点です。マルチコアプロセッサ。 JSR 335=ラムダ式 + インターフェイスの改善 (デフォルトのメソッド) + バッチ データ操作。前の 2 つの記事と合わせて、JSR335 の関連内容を完全に学習しました。

2. 外部反復 VS 内部反復

以前は、Java コレクションは内部反復を表現できず、外部反復メソッド、つまり for ループまたは while ループのみを提供していました。

List persons = asList(new Person("Joe"), new Person("Jim"), new Person("John"));
for (Person p :  persons) {
   p.setLastName("Doe");
}

上記の例は、いわゆる外部反復である以前のアプローチです。ループは固定シーケンス ループです。今日のマルチコア時代では、ループを並列化したい場合は、上記のコードを変更する必要があります。効率がどの程度向上するかはまだ不確実であり、一定のリスク (スレッドの安全性の問題など) が生じる可能性があります。

内部反復を記述するには、Lambda のようなクラス ライブラリを使用する必要があります。lambda と Collection.forEach を使用して上記のループを書き換えましょう

persons.forEach(p->p.setLastName("Doe"));

これで、JDK ライブラリがループを制御するので、最後のループを気にする必要はありません。 name.name が各人物オブジェクトに設定されると、ライブラリは実行環境、並列読み込み、順不同読み込み、または遅延読み込みに応じてその方法を決定できます。これは内部反復であり、クライアントは動作 p.setLastName をデータとして API に渡します。

内部反復は、実際にはコレクションのバッチ操作とは密接に関係していませんが、文法表現の変化を感じることができます。バッチ操作に関連して本当に興味深いのは、新しいストリーム API です。新しい java.util.stream パッケージが JDK 8 に追加されました。


3. ストリーム API

ストリームはデータ ストリームを表すだけであり、データ構造を持たないため、一度走査した後は再度走査することはできません (コレクションとは異なり、プログラミング時にこれに注意する必要があります)何度走査されてもデータはまだ存在します)、そのソースはコレクション、配列、io などです。

3.1 中間メソッドとエンドポイントメソッド

フローの機能は、ビッグデータを操作するためのインターフェイスを提供し、データ操作をより簡単かつ高速にすることです。フィルタリング、マッピング、トラバーサル数の削減などのメソッドがあり、これらのメソッドは中間メソッドと終端メソッドの 2 つのタイプに分けられます。中間メソッドは本質的に連続的なものである必要があります。最終結果を取得したい場合は、エンドポイント操作を使用して、ストリームによって生成された最終結果を収集する必要があります。これら 2 つのメソッドの違いは、戻り値に注目することです。ストリームの場合は中間メソッドであり、そうでない場合は終了メソッドです。詳細については、Stream の API を参照してください。

いくつかの中間メソッド (フィルター、マップ) とエンドポイント メソッド (収集、合計) の簡単な紹介

3.1.1フィルター

データ ストリームにフィルター関数を実装することは、最初に考えることができる最も自然な操作です。 Stream インターフェイスは、フィルター条件を定義するラムダ式を使用する操作を表す Predicate 実装を受け入れるフィルター メソッドを公開します。

List persons = …
Stream personsOver18 = persons.stream().filter(p -> p.getAge() > 18);//过滤18岁以上的人

3.1.2Map

オブジェクトの変換時などに、いくつかのデータをフィルタリングするとします。 Map オペレーションを使用すると、入力パラメータを受け入れて返す Function 実装 (Function の一般的な T と R はそれぞれ実行入力と実行結果を表します) を実行できます。まず、匿名内部クラスの形式でそれを記述する方法を見てみましょう:

Stream adult= persons
              .stream()
              .filter(p -> p.getAge() > 18)
              .map(new Function() {
                  @Override
                  public Adult apply(Person person) {
                     return new Adult(person);//将大于18岁的人转为成年人
                  }
              });

次に、上記の例をラムダ式に変換します:

Stream map = persons.stream()
                    .filter(p -> p.getAge() > 18)
                    .map(person -> new Adult(person));

3.1.3Count

count メソッドはストリーム メソッドのエンドポイントです。 、フロー結果の最終統計を作成し、int を返すことができます。たとえば、18 歳以上の人の合計数を計算してみましょう:

int countOfAdult=persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .count();

3.1.4Collect

collect メソッドもエンドポイントです。最終結果を収集できるフローのメソッド

List adultList= persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .collect(Collectors.toList());

または、結果を収集するために特定の実装クラスを使用したい場合:

List adultList = persons
                 .stream()
                 .filter(p -> p.getAge() > 18)
                 .map(person -> new Adult(person))
                 .collect(Collectors.toCollection(ArrayList::new));

スペースが限られており、他の中間メソッドとエンドポイントメソッドは導入されません上記の例を 1 つずつ読んだ後、これら 2 つの方法の違いを理解でき、後で必要に応じて使用できます。

3.2 順次ストリームと並列ストリーム

各ストリームには、順次実行と並列実行の 2 つのモードがあります。
逐次フロー:

List <Person> people = list.getStream.collect(Collectors.toList());

並列フロー:

List <Person> people = list.getStream.parallel().collect(Collectors.toList());

その名前が示すように、逐次メソッドを使用してトラバースする場合は、次の項目を読み取る前に各項目を読み取ります。並列トラバーサルを使用する場合、配列は複数のセグメントに分割され、それぞれが異なるスレッドで処理され、結果がまとめて出力されます。

3.2.1 並列フローの原理:

List originalList = someData;
split1 = originalList(0, mid);//将数据分小部分
split2 = originalList(mid,end);
new Runnable(split1.process());//小部分执行操作
new Runnable(split2.process());
List revisedList = split1 + split2;//将结果合并

3.2.2 シーケンシャルとパラレルのパフォーマンステストの比較

マルチコアマシンの場合、理論的にはパラレルフローはシーケンシャルフローの2倍の速度になります。テストコード

long t0 = System.nanoTime();
//初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法
int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray();
long t1 = System.nanoTime();
//和上面功能一样,这里是用并行流来计算
int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray();
long t2 = System.nanoTime();
//我本机的结果是serial: 0.06s, parallel 0.02s,证明并行流确实比顺序流快
System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);

3.3 Folk/Joinフレームワークについて

アプリケーション ハードウェアの並列処理は Java 7 で利用できます。java.util.concurrent パッケージの新機能の 1 つは、非常に強力で効率的なもので、興味のある学生はそれを学ぶことができます。ここでは詳細には触れませんが、Stream.Parallel() と比較すると、後者の方が好きです。

4. 概要

ラムダがない場合、Stream は、上記の 3.1.2map の例のように、大量の匿名内部クラスを生成します。コレクション フレームワークでは必然的に多くの変更が発生するため、Lambda+default メソッドにより JDK ライブラリがより強力かつ柔軟になるのは、ストリームとコレクション フレームワークの改善が何よりの証拠です。

Java8 ラムダ式の新機能 (使用例) に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。