導入
ラムダ式は Java SE 8 の重要な新機能です。ラムダ式を使用すると、関数インターフェイスを式で置き換えることができます。ラムダ式はメソッドと同じで、通常のパラメータ リストと、これらのパラメータを使用する本体 (式またはコード ブロック) を提供します。
ラムダ式はコレクション ライブラリも強化します。 Java SE 8 では、コレクション データに対するバッチ操作用の 2 つのパッケージ (java.util.function パッケージと java.util.stream パッケージ) が追加されています。 ストリームはイテレータに似ていますが、多くの追加機能があります。 全体として、ラムダ式とストリームは、Java 言語にジェネリックスとアノテーションが追加されて以来の最大の変更です。 この記事では、単純な例から複雑な例まで、ラムダ式とストリームの威力を見ていきます。
環境整備
Java 8 がインストールされていない場合は、ラムダとストリームを使用する前に、まず Java 8 をインストールする必要があります (翻訳者は、テスト用に仮想マシンに Java 8 をインストールすることを推奨しています)。 NetBeans や IntelliJ IDEA などのツールや IDE は、ラムダ式、反復可能な注釈、コンパクト プロファイル、その他の機能を含む Java 8 機能をサポートします。
ラムダ式の構文
基本的な構文:
(パラメータ) -> 式
または
(パラメータ) ->{ ステートメント }
Java ラムダ式の簡単な例を次に示します。
次のようにコードをコピーします。
// 1. パラメータは必要ありません。戻り値は 5 です。
() -> 5
// 2. パラメータ(数値型)を受け取り、その値の2倍を返す
x -> 2*x
// 3. 2 つのパラメータ (数値) を受け入れ、それらの差を返します
(x, y) -> xy
// 4. int型整数を2つ受け取り、その合計を返す
(int x, int y) -> x + y
// 5. 文字列オブジェクトを受け入れ、値を返さずにコンソールに出力します (void を返すように見えます)。
(文字列) -> System.out.print(s)
基本的なラムダの例
ラムダ式が何であるかを理解したところで、いくつかの基本的な例から始めましょう。 このセクションでは、ラムダ式がコーディング方法にどのような影響を与えるかを見ていきます。 プレーヤーのリストがあると仮定すると、プログラマは for ステートメント (「for ループ」) を使用してトラバースすることができ、これは Java SE 8 の別の形式に変換できます。
次のようにコードをコピーします。
String[] atp = {"ラファエル・ナダル", "ノバク・ジョコビッチ",
「スタニスラス・ワウリンカ」、
「デビッド・フェレール」、「ロジャー・フェデラー」、
「アンディ・マレー」、「トーマス・ベルディヒ」、
"フアン マルティン デル ポトロ"};
List<String> プレーヤー = Arrays.asList(atp);
//前のループメソッド
for (文字列プレーヤー : プレーヤー) {
System.out.print(プレイヤー + "; ");
}
//ラムダ式と関数演算を使用する
player.forEach((プレイヤー) -> System.out.print(プレイヤー + "; "));
//Java 8 では二重コロン演算子を使用する
player.forEach(System.out::println);
ご覧のとおり、ラムダ式を使用するとコードを 1 行に減らすことができます。 もう 1 つの例は、匿名クラスをラムダ式で置き換えることができるグラフィカル ユーザー インターフェイス プログラムです。 同様に、Runnable インターフェイスを実装するときに次のように使用することもできます。
次のようにコードをコピーします。
//匿名の内部クラスを使用する
btn.setOnAction(new EventHandler<ActionEvent>() {
@オーバーライド
public void handle(ActionEvent イベント) {
System.out.println("Hello World!");
}
});
// またはラムダ式を使用する
btn.setOnAction(event -> System.out.println("Hello World!"));
以下は、ラムダを使用して Runnable インターフェイスを実装する例です。
次のようにコードをコピーします。
// 1.1 匿名内部クラスを使用する
new Thread(new Runnable() {
@オーバーライド
public void run() {
System.out.println("Hello world!");
}
})。始める();
// 1.2 ラムダ式を使用する
new Thread(() -> System.out.println("Hello world!")).start();
// 2.1 匿名内部クラスを使用する
実行可能なレース 1 = new Runnable() {
@オーバーライド
public void run() {
System.out.println("Hello world!");
}
};
// 2.2 ラムダ式を使用する
実行可能なrace2 = () -> System.out.println("Hello world!");
// run メソッドを直接呼び出します (新しいスレッドは開かれません!)
レース1.run();
レース2.run();
Runnable のラムダ式はブロック形式を使用して、5 行のコードを 1 行のステートメントに変換します。 次に、次のセクションでは、ラムダを使用してコレクションを並べ替えます。
Lambda を使用したコレクションの並べ替え
Java では、コレクションをソートするために Comparator クラスが使用されます。 以下の例では、プレイヤーの名前、姓、名前の長さ、最後の文字に基づいています。 前の例と同様に、最初に匿名内部クラスを使用して並べ替え、次にラムダ式を使用してコードを合理化します。
最初の例では、リストを名前で並べ替えます。 古い方法を使用すると、コードは次のようになります。
次のようにコードをコピーします。
String[] player = {"ラファエル・ナダル", "ノバク・ジョコビッチ",
「スタニスラス・ワウリンカ」、「デビッド・フェレール」、
「ロジャー・フェデラー」、「アンディ・マレー」、
「トーマス・ベルディヒ」、「フアン・マルティン・デル・ポトロ」、
"リシャール・ガスケ"、"ジョン・イズナー"};
// 1.1 匿名の内部クラスを使用してプレーヤーを名前に従って並べ替えます
Arrays.sort(players, new Comparator<String>() {
@オーバーライド
public int Compare(String s1, String s2) {
return (s1.compareTo(s2));
}
});
ラムダを使用すると、次のコードで同じ機能を実現できます。
次のようにコードをコピーします。
// 1.2 ラムダ式を使用してプレイヤーを並べ替える
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
Arrays.sort(players, sortByName);
// 1.3 は次の形式をとることもできます。
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));
その他のランキングは以下の通り。 上の例と同様に、コードは匿名の内部クラスといくつかのラムダ式を通じて Comparator を実装します。
次のようにコードをコピーします。
// 1.1 匿名の内部クラスを使用してプレイヤーを姓に基づいて並べ替えます
Arrays.sort(players, new Comparator<String>() {
@オーバーライド
public int Compare(String s1, String s2) {
return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" ")));
}
});
// 1.2 ラムダ式を使用して姓に従って並べ替えます
Comparator<String> sortBySurname = (String s1, String s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) );
Arrays.sort(players, sortBySurname);
// 1.3 というか、原作者が間違えたのか、括弧が多すぎる…。
Arrays.sort(players, (文字列 s1, 文字列 s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) )
);
// 2.1 匿名の内部クラスを使用して、名前の長さに従ってプレーヤーを並べ替えます
Arrays.sort(players, new Comparator<String>() {
@オーバーライド
public int Compare(String s1, String s2) {
return (s1.length() - s2.length());
}
});
// 2.2 ラムダ式を使用して名前の長さに応じて並べ替える
Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
Arrays.sort(players, sortByNameLenght);
// 2.3 以降
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));
// 3.1 匿名の内部クラスを使用して、最後の文字に従ってプレイヤーを並べ替えます
Arrays.sort(players, new Comparator<String>() {
@オーバーライド
public int Compare(String s1, String s2) {
return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
}
});
// 3.2 ラムダ式を使用して最後の文字に従って並べ替える
Comparator<String> sortByLastLetter =
(文字列 s1、文字列 s2) ->
(s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
Arrays.sort(players, sortByLastLetter);
// 3.3 以降
Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
シンプルかつ直感的です。 次のセクションでは、ラムダの機能をさらに詳しく調べ、それらをストリームで使用します。
ラムダとストリームの使用
ストリームはコレクションのラッパーであり、通常はラムダと一緒に使用されます。 ラムダを使用すると、マップ、フィルター、制限、並べ替え、カウント、最小、最大、合計、収集などの多くの操作をサポートできます。 同様に、ストリームは遅延操作を使用し、実際にはすべてのデータを読み取るわけではなく、getFirst() などのメソッドが発生するとチェーン構文は終了します。 次の例では、ラムダとストリームで何ができるかを見ていきます。 Person クラスを作成し、このクラスを使用してリストにデータを追加しました。このデータはその後のストリーミング操作に使用されます。 person は単純な POJO クラスです。
次のようにコードをコピーします。
パブリック クラス 人 {
private String 名、姓、職業、性別。
民間の給与、年齢。
public Person(String firstName, String lastName, String job,
性別、年齢、給与の文字列) {
this.firstName = 名;
this.lastName = 姓;
this.gender = 性別;
this.age = 年齢;
this.job = 仕事;
this.salary = 給与;
}
// ゲッターとセッター
// 。
}
次に、2 つのリストを作成します。どちらも Person オブジェクトを格納するために使用されます。
次のようにコードをコピーします。
List<人> javaProgrammers = new ArrayList<人>() {
{
add(new Person("エルスドン", "ジェイコブ", "Java プログラマー", "男性", 43, 2000));
add(new 人("タムセン", "ブリタニー", "Java プログラマー", "女性", 23, 1500));
add(new 人("フロイド", "ドニー", "Java プログラマー", "男性", 33, 1800));
add(new 人("シンディ", "ジョニー", "Java プログラマー", "女性", 32, 1600));
add(new Person("Vere", "Hervey", "Java プログラマー", "男性", 22, 1200));
add(new Person("モード", "ジェイミー", "Java プログラマー", "女性", 27, 1900));
add(new 人("ショーン", "ランドール", "Java プログラマー", "男性", 30, 2300));
add(new 人("ジェイデン", "コリーナ", "Java プログラマー", "女性", 35, 1700));
add(new Person("パーマー", "ディーン", "Java プログラマー", "男性", 33, 2000));
add(new 人("アディソン", "パム", "Java プログラマー", "女性", 34, 1300));
}
};
List<人> phpProgrammers = new ArrayList<人>() {
{
add(new 人("ジャロッド", "ペース", "PHP プログラマー", "男性", 34, 1550));
add(new 人("クラレット", "シシリー", "PHP プログラマー", "女性", 23, 1200));
add(new 人("ビクター", "チャニング", "PHP プログラマー", "男性", 32, 1600));
add(new 人("トリ", "シェリル", "PHP プログラマー", "女性", 21, 1000));
add(new 人("オズボーン", "シャッド", "PHP プログラマー", "男性", 32, 1100));
add(new 人("ロザリンド", "レイラ", "PHP プログラマー", "女性", 25, 1300));
add(new 人("フレイザー", "ヒューイ", "PHP プログラマー", "男性", 36, 1100));
add(new 人("クイン", "タマラ", "PHP プログラマー", "女性", 21, 1000));
add(new 人("アルビン", "ランス", "PHP プログラマー", "男性", 38, 1600));
add(new 人("エボンヌ", "シャリ", "PHP プログラマー", "女性", 40, 1800));
}
};
ここで、forEach メソッドを使用して上記のリストを反復して出力します。
次のようにコードをコピーします。
System.out.println("すべてのプログラマの名前:");
javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
また、forEach メソッドを使用して、プログラマーの給与を 5% 増加させます。
次のようにコードをコピーします。
System.out.println("プログラマーの給与を 5% 増額します:");
Consumer<Personal> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);
もう 1 つの便利なメソッドは filter() です。これを使用すると、月給が 1400 ドルを超える PHP プログラマーを表示できます。
次のようにコードをコピーします。
System.out.println("月給 1,400 ドル以上の PHP プログラマーは次のとおりです:")
phpProgrammers.stream()
.filter((p) -> (p.getSalary() > 1400))
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
フィルターを定義し、それを再利用して他の操作を実行することもできます。
次のようにコードをコピーします。
// フィルターを定義する
Predicate<人> ageFilter = (p) -> (p.getAge() > 25);
Predicate<人>給与フィルター = (p) -> (p.getSalary() > 1400);
Predicate<人> ジェンダーフィルター = (p) -> ("女性".equals(p.getGender()));
System.out.println("ここでは、24 歳以上で月給 1,400 ドル以上の女性 PHP プログラマーを紹介します:");
phpProgrammers.stream()
.filter(年齢フィルター)
.filter(給与フィルター)
.filter(性別フィルター)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
//フィルターを再利用する
System.out.println("24 歳以上の女性 Java プログラマー:");
javaProgrammers.stream()
.filter(年齢フィルター)
.filter(性別フィルター)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
結果セットの数を制限するには、limit メソッドを使用します。
次のようにコードをコピーします。
System.out.println("最初の 3 人の Java プログラマー:");
javaProgrammers.stream()
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
System.out.println("女性 Java プログラマー トップ 3:");
javaProgrammers.stream()
.filter(性別フィルター)
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
並べ替えについてはどうすればよいでしょうか? 答えは「はい」です。 次の例では、Java プログラマーを名前と給与でソートし、リストに入れて、リストを表示します。
次のようにコードをコピーします。
System.out.println("名前順に並べ替えて、上位 5 人の Java プログラマを表示します:");
List<人>sortedJavaProgrammers = javaProgrammers
。ストリーム()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.limit(5)
.collect(toList());
sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
System.out.println("給与に従って Java プログラマーを並べ替えます:");
sortedJavaProgrammers = javaProgrammers
。ストリーム()
.sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
.collect( toList() );
sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
最低給与と最高給与のみに興味がある場合、並べ替え後に最初と最後を選択するよりも速いのは、min メソッドと max メソッドです。
次のようにコードをコピーします。
System.out.println("最も給料の低い Java プログラマー:");
人当たり = javaProgrammers
。ストリーム()
.min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
。得る()
System.out.printf("名前: %s %s; 給与: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())
System.out.println("最も給与が高い Java プログラマー:");
人 人 = javaProgrammers
。ストリーム()
.max((p, p2) -> (p.getSalary() - p2.getSalary()))
。得る()
System.out.printf("名前: %s %s; 給与: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())
上の例では、collect メソッドがどのように機能するかを見てきました。 map メソッドと組み合わせて、collect メソッドを使用して、結果セットを String、Set、または TreeSet に入れることができます。
次のようにコードをコピーします。
System.out.println("PHP プログラマーの名を文字列に連結します:");
文字列 phpDevelopers = phpProgrammers
。ストリーム()
.map(人::get名)
.collect(joining(" ; ")); // 以降の操作でトークンとして使用できます。
System.out.println("Java プログラマーの名を Set に保存:");
Set<String> javaDevFirstName = javaProgrammers
。ストリーム()
.map(人::get名)
.collect(toSet());
System.out.println("Java プログラマーの名前を TreeSet に保存:");
TreeSet<String> javaDevLastName = javaProgrammers
。ストリーム()
.map(人::get姓)
.collect(toCollection(TreeSet::new));
ストリームは並列にすることもできます。 例は次のとおりです。
次のようにコードをコピーします。
System.out.println("Java プログラマーに支払われた金額をすべて計算します:");
int totalSalary = javaProgrammers
.ParallelStream()
.mapToInt(p -> p.getSalary())
。和();
summaryStatistics メソッドを使用して、ストリーム内の要素のさまざまな概要データを取得できます。 次に、getMax、getMin、getSum、getAverage などのメソッドにアクセスできます。
次のようにコードをコピーします。
//数値のカウント、最小、最大、合計、平均を計算します
List<Integer> 数値 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntsummaryStatistics 統計 = 数値
。ストリーム()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("リスト内の最大の数値: " + stats.getMax());
System.out.println("リスト内の最小の数値: " + stats.getMin());
System.out.println("すべての数値の合計: " + stats.getSum());
System.out.println("すべての数値の平均: " + stats.getAverage());
OK、以上です。気に入っていただければ幸いです。
要約する
この記事では、基本的な例からラムダとストリームを使用したより複雑な例まで、ラムダ式のさまざまな使用方法を学びました。 さらに、ラムダ式と Comparator クラスを使用して Java コレクションを並べ替える方法も学びました。
翻訳者注: 非常に高度に見えますが、ラムダ式の本質は、コンパイラによって推論される単なる「構文糖」であり、通常のコードに変換してラップするのに役立ちます。そのため、同じ機能を実現するために使用するコードの量を減らすことができます。 。非常に高度なハッカーが書いたようなコードなので、むやみに使用しないことをお勧めします。簡潔でわかりにくく、デバッグが難しく、保守スタッフに叱られたくなります。