Java 8 – Lambdasとの強力な比較
1. 概要
このチュートリアルでは、Lambda support in Java 8 – specifically at how to leverage it to write the Comparator and sort a Collectionを最初に見ていきます。
この記事は、例としてここのthe “Java – Back to Basic” seriesの一部です。
参考文献:
ラムダ式と機能的インターフェース:ヒントとベストプラクティス
Java 8ラムダと機能インターフェースの使用に関するヒントとベストプラクティス。
まず、単純なエンティティクラスを定義しましょう。
public class Human {
private String name;
private int age;
// standard constructors, getters/setters, equals and hashcode
}
2. ラムダなしの基本的な並べ替え
Java 8より前は、コレクションのソートには、ソートで使用されるcreating an anonymous inner class for the Comparatorが含まれていました。
new Comparator() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
}
これは、HumanエンティティのListを並べ替えるために使用されます。
@Test
public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
Collections.sort(humans, new Comparator() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
});
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
3. ラムダサポート付きの基本的な並べ替え
Lambdasの導入により、匿名内部クラスをバイパスして、simple, functional semanticsで同じ結果を達成できるようになりました。
(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());
同様に、以前と同様に動作をテストできるようになりました。
@Test
public void whenSortingEntitiesByName_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
humans.sort(
(Human h1, Human h2) -> h1.getName().compareTo(h2.getName()));
assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
古いCollections.sort APIの代わりにthe new sort API added to java.util.List in Java 8も使用していることに注意してください。
4. タイプ定義のない基本的なソート
タイプ定義を指定しないことで、式をさらに簡略化できます–the compiler is capable of inferring these自体:
(h1, h2) -> h1.getName().compareTo(h2.getName())
繰り返しますが、テストは非常によく似ています。
@Test
public void
givenLambdaShortForm_whenSortingEntitiesByName_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
5. 静的メソッドへの参照を使用して並べ替え
次に、静的メソッドを参照するLambda式を使用して並べ替えを実行します。
まず、メソッドcompareByNameThenAgeを定義します–Comparator<Human>オブジェクトのcompareメソッドとまったく同じシグネチャを使用します。
public static int compareByNameThenAge(Human lhs, Human rhs) {
if (lhs.name.equals(rhs.name)) {
return lhs.age - rhs.age;
} else {
return lhs.name.compareTo(rhs.name);
}
}
次に、次の参照を使用してhumans.sortメソッドを呼び出します。
humans.sort(Human::compareByNameThenAge);
最終結果は、静的メソッドをComparatorとして使用してコレクションを実際に並べ替えることです。
@Test
public void
givenMethodDefinition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
humans.sort(Human::compareByNameThenAge);
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
6. 抽出されたコンパレータを並べ替える
また、instance method referenceおよびComparator.comparingメソッドを使用して比較ロジック自体を定義することを回避することもできます。このメソッドは、その関数に基づいてComparableを抽出および作成します。
ゲッターgetName()を使用してLambda式を作成し、リストを名前で並べ替えます。
@Test
public void
givenInstanceMethod_whenSortingEntitiesByName_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
Collections.sort(
humans, Comparator.comparing(Human::getName));
assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
7. 逆ソート
JDK 8では、reversing the comparatorのヘルパーメソッドも導入されています。これをすばやく利用して、並べ替えを逆にすることができます。
@Test
public void whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
Comparator comparator
= (h1, h2) -> h1.getName().compareTo(h2.getName());
humans.sort(comparator.reversed());
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
8. 複数の条件で並べ替え
比較ラムダ式は、これほど単純である必要はありません。たとえば、エンティティを最初に名前で、次に年齢で並べ替えるなど、more complex expressions as wellを記述できます。
@Test
public void whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 12),
new Human("Sarah", 10),
new Human("Zack", 12)
);
humans.sort((lhs, rhs) -> {
if (lhs.getName().equals(rhs.getName())) {
return lhs.getAge() - rhs.getAge();
} else {
return lhs.getName().compareTo(rhs.getName());
}
});
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
9. 複数の条件で並べ替え–構成
同じ比較ロジック(最初に名前でソートし、次に年齢でソートする)も、Comparatorの新しい構成サポートによって実装できます。
より複雑な比較ロジックを構築するためのStarting with JDK 8, we can now chain together multiple comparators:
@Test
public void
givenComposition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List humans = Lists.newArrayList(
new Human("Sarah", 12),
new Human("Sarah", 10),
new Human("Zack", 12)
);
humans.sort(
Comparator.comparing(Human::getName).thenComparing(Human::getAge)
);
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
10. Stream.sorted()でリストを並べ替える
Java 8のStreamsorted()APIを使用してコレクションを並べ替えることもできます。
自然な順序とComparator.によって提供される順序を使用してストリームを並べ替えることができます。このために、sorted()APIの2つのオーバーロードされたバリアントがあります。
-
sorted()–は、自然な順序を使用してStreamの要素を並べ替えます。要素クラスは、Comparableインターフェースを実装する必要があります。
-
sorted(Comparator<?superT>comparator) –Comparatorインスタンスに基づいて要素を並べ替えます
use the sorted() method with natural orderingの方法の例を見てみましょう。
@Test
public final void
givenStreamNaturalOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
List letters = Lists.newArrayList("B", "A", "C");
List sortedLetters = letters.stream().sorted().collect(Collectors.toList());
assertThat(sortedLetters.get(0), equalTo("A"));
}
では、use a custom Comparator with the sorted() APIを実行する方法を見てみましょう。
@Test
public final void
givenStreamCustomOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
Comparator nameComparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
List sortedHumans =
humans.stream().sorted(nameComparator).collect(Collectors.toList());
assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}
use the Comparator.comparing() methodの場合、上記の例をさらに単純化できます。
@Test
public final void
givenStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
List sortedHumans = humans.stream()
.sorted(Comparator.comparing(Human::getName))
.collect(Collectors.toList());
assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}
11. Stream.sorted()を使用してリストを逆に並べ替える
Stream.sorted()を使用して、コレクションを逆に並べ替えることもできます。
まず、combine the sorted()method with Comparator.reverseOrder() to sort a list in reverse natural orderの方法の例を見てみましょう。
@Test
public final void
givenStreamNaturalOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List letters = Lists.newArrayList("B", "A", "C");
List reverseSortedLetters = letters.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
assertThat(reverseSortedLetters.get(0), equalTo("C"));
}
それでは、use the sorted() method and a custom Comparatorを実行する方法を見てみましょう。
@Test
public final void
givenStreamCustomOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
Comparator reverseNameComparator =
(h1, h2) -> h2.getName().compareTo(h1.getName());
List reverseSortedHumans = humans.stream().sorted(reverseNameComparator)
.collect(Collectors.toList());
assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}
compareToの呼び出しが反転していることに注意してください。これは、反転を実行しているものです。
最後に、上記の例をusing the Comparator.comparing() methodで簡略化しましょう。
@Test
public final void
givenStreamComparatorOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
List reverseSortedHumans = humans.stream()
.sorted(Comparator.comparing(Human::getName, Comparator.reverseOrder()))
.collect(Collectors.toList());
assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}
12. 結論
この記事では、List can be sorted using Java 8 Lambda Expressionsが構文糖衣構文を超えて実際の強力な機能セマンティクスに移行するさまざまでエキサイティングな方法を説明しました。
これらすべての例とコードスニペットcan be found in the GitHub projectの実装–これはEclipseベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。