詳解Java8如何使用Lambda表達(dá)式進(jìn)行比較
在Java 8之前,對(duì)集合進(jìn)行排序需要為排序中使用的比較器 Comparator 創(chuàng)建一個(gè)匿名內(nèi)部類:
new Comparator<Human>() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
}這僅用于對(duì) Human 實(shí)體列表進(jìn)行排序:
@Test
public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
Collections.sort(humans, new Comparator<Human>() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
});
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}支持Lambda的基本排序
隨著Lambdas的引入,我們現(xiàn)在可以繞過匿名內(nèi)部類,通過簡(jiǎn)單的函數(shù)語義實(shí)現(xiàn)相同的結(jié)果:
(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());
類似地,我們現(xiàn)在可以像以前一樣測(cè)試行為:
@Test
public void whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> 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)));
}注意,我們還使用添加到j(luò)ava中的新排序API。 java.util.List 使用Java8而不是舊集合列出 Collections.sort 。
無類型定義的基本排序
我們可以通過不指定類型定義來進(jìn)一步簡(jiǎn)化表達(dá)式;編譯器能夠自行推斷出以下各項(xiàng):
(h1, h2) -> h1.getName().compareTo(h2.getName())
同樣,測(cè)試仍然非常相似:
@Test
public void
givenLambdaShortForm_whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> 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)));
}使用引用靜態(tài)方法進(jìn)行排序
接下來,我們將使用Lambda表達(dá)式和對(duì)靜態(tài)方法的引用來執(zhí)行排序。
首先,我們將使用與 Comparator<Human> 對(duì)象中的 compare 方法完全相同的簽名來定義 CompareBynametEnable 方法:
public static int compareByNameThenAge(Human lhs, Human rhs) {
if (lhs.name.equals(rhs.name)) {
return Integer.compare(lhs.age, rhs.age);
} else {
return lhs.name.compareTo(rhs.name);
}
}然后我們要調(diào)用 humans.sort 使用此引用的排序方法:
humans.sort(Human::compareByNameThenAge);
最終結(jié)果是使用靜態(tài)方法作為比較器對(duì)集合進(jìn)行工作排序:
@Test
public void
givenMethodDefinition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List<Human> 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)));
}Sort Extracted Comparators
通過使用實(shí)例方法引用和比較器,我們甚至可以避免定義比較邏輯本身。比較方法,該方法基于該函數(shù)提取并創(chuàng)建可比較的。
我們將使用 getName() 來構(gòu)建Lambda表達(dá)式并按名稱對(duì)列表進(jìn)行排序:
@Test
public void
givenInstanceMethod_whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> 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)));
}反向排序
JDK 8還引入了一種輔助方法來反轉(zhuǎn)比較器。我們可以很快利用這一點(diǎn)來逆轉(zhuǎn)我們的排序:
@Test
public void whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(
new Human("Sarah", 10),
new Human("Jack", 12)
);
Comparator<Human> comparator
= (h1, h2) -> h1.getName().compareTo(h2.getName());
humans.sort(comparator.reversed());
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}使用多個(gè)條件進(jìn)行排序
比較lambda表達(dá)式不必如此簡(jiǎn)單。我們還可以編寫更復(fù)雜的表達(dá)式,例如,先按名稱,然后按年齡對(duì)實(shí)體進(jìn)行排序:
@Test
public void whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List<Human> 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 Integer.compare(lhs.getAge(), rhs.getAge());
} else {
return lhs.getName().compareTo(rhs.getName());
}
});
Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}使用多個(gè)條件排序-組合
同樣的比較邏輯,首先按名稱排序,然后按年齡排序,也可以通過新的composition支持來實(shí)現(xiàn)。
從JDK 8開始,我們現(xiàn)在可以將多個(gè)比較器鏈接在一起,以構(gòu)建更復(fù)雜的比較邏輯:
@Test
public void
givenComposition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
List<Human> 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)));
}使用Stream.sorted()對(duì)列表進(jìn)行排序
我們還可以使用Java 8的Stream sorted()API對(duì)集合進(jìn)行排序。
我們可以使用自然排序以及比較器提供的排序?qū)α鬟M(jìn)行排序。為此,我們有兩個(gè)sorted()API的重載變體:
- sorted()–使用自然排序?qū)α鞯脑剡M(jìn)行排序;元素類必須實(shí)現(xiàn)可比較的接口。
- sorted(Comparator<? super T> comparator)–基于Comparator實(shí)例對(duì)元素進(jìn)行排序
讓我們看一個(gè)如何將sorted()方法用于自然排序的示例:
@Test
public final void
givenStreamNaturalOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
List<String> letters = Lists.newArrayList("B", "A", "C");
List<String> sortedLetters = letters.stream().sorted().collect(Collectors.toList());
assertThat(sortedLetters.get(0), equalTo("A"));
}現(xiàn)在,讓我們看看如何在 sorted() API中使用自定義比較器:
@Test
public final void
givenStreamCustomOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
Comparator<Human> nameComparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
List<Human> sortedHumans =
humans.stream().sorted(nameComparator).collect(Collectors.toList());
assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}如果使用比較器,我們可以進(jìn)一步簡(jiǎn)化上述示例。 Comparator.comparing() 方法:
@Test
public final void
givenStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
List<Human> sortedHumans = humans.stream()
.sorted(Comparator.comparing(Human::getName))
.collect(Collectors.toList());
assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}使用Stream.sorted()對(duì)列表進(jìn)行反向排序
我們也可以使用 Stream.sorted() 對(duì)集合進(jìn)行反向排序。
首先,讓我們看一個(gè)如何將sorted()方法與Comparator相結(jié)合的示例。reverseOrder()按與自然順序相反的順序?qū)α斜砼判颍?/p>
@Test
public final void
givenStreamNaturalOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List<String> letters = Lists.newArrayList("B", "A", "C");
List<String> reverseSortedLetters = letters.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
assertThat(reverseSortedLetters.get(0), equalTo("C"));
}現(xiàn)在讓我們看看如何使用sorted()方法和自定義比較器:
@Test
public final void
givenStreamCustomOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
Comparator<Human> reverseNameComparator =
(h1, h2) -> h2.getName().compareTo(h1.getName());
List<Human> reverseSortedHumans = humans.stream().sorted(reverseNameComparator)
.collect(Collectors.toList());
assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}請(qǐng)注意,compareTo的調(diào)用是翻轉(zhuǎn)的,它負(fù)責(zé)反轉(zhuǎn)。
最后,讓我們使用比較器簡(jiǎn)化上述示例。Comparator.comparing() 方法:
@Test
public final void
givenStreamComparatorOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
List<Human> reverseSortedHumans = humans.stream()
.sorted(Comparator.comparing(Human::getName, Comparator.reverseOrder()))
.collect(Collectors.toList());
assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}Null值
到目前為止,我們實(shí)現(xiàn)的比較器不能對(duì)包含空值的集合進(jìn)行排序。也就是說,如果集合至少包含一個(gè)null元素,則sort方法會(huì)引發(fā) NullPointerException :
@Test(expected = NullPointerException.class)
public void givenANullElement_whenSortingEntitiesByName_thenThrowsNPE() {
List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12));
humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
}最簡(jiǎn)單的解決方案是在比較器實(shí)現(xiàn)中手動(dòng)處理空值:
@Test
public void givenANullElement_whenSortingEntitiesByNameManually_thenMovesTheNullToLast() {
List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12), null);
humans.sort((h1, h2) -> {
if (h1 == null) {
return h2 == null ? 0 : 1;
}
else if (h2 == null) {
return -1;
}
return h1.getName().compareTo(h2.getName());
});
Assert.assertNotNull(humans.get(0));
Assert.assertNull(humans.get(1));
Assert.assertNull(humans.get(2));
}在這里,我們將所有空元素推向集合的末尾。為此,比較器將null視為大于非null值。如果兩者都為空,則認(rèn)為它們相等。
此外,我們可以將任何非空安全的比較器 Comparator.nullsLast() 方法,并獲得相同的結(jié)果:
@Test
public void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToLast() {
List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12), null);
humans.sort(Comparator.nullsLast(Comparator.comparing(Human::getName)));
Assert.assertNotNull(humans.get(0));
Assert.assertNull(humans.get(1));
Assert.assertNull(humans.get(2));
}同樣,我們可以使用 Comparator.nullsFirst() 將null元素移到集合的開頭:
@Test
public void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToStart() {
List<Human> humans = Lists.newArrayList(null, new Human("Jack", 12), null);
humans.sort(Comparator.nullsFirst(Comparator.comparing(Human::getName)));
Assert.assertNull(humans.get(0));
Assert.assertNull(humans.get(1));
Assert.assertNotNull(humans.get(2));
}強(qiáng)烈建議使用 nullsFirst() 或 nullsLast() 修飾符,因?yàn)樗鼈兏`活、可讀性更強(qiáng)。
結(jié)論
本文演示了使用Java8 Lambda表達(dá)式對(duì)列表進(jìn)行排序的各種令人興奮的方式,這些方式超越了語法糖,轉(zhuǎn)變?yōu)檎嬲龔?qiáng)大的函數(shù)語義。
所有這些示例和代碼片段的實(shí)現(xiàn)都可以在GitHub上找到: https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-lambdas
到此這篇關(guān)于詳解Java8如何使用Lambda表達(dá)式進(jìn)行比較的文章就介紹到這了,更多相關(guān)Java8 Lambda表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解SpringBoot2 使用Spring Session集群
這篇文章主要介紹了SpringBoot2 使用Spring Session集群,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-04-04
java實(shí)現(xiàn)注冊(cè)登錄系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)注冊(cè)登錄系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Intellij IDEA中啟動(dòng)多個(gè)微服務(wù)(開啟Run Dashboard管理)
這篇文章主要介紹了Intellij IDEA中啟動(dòng)多個(gè)微服務(wù)(開啟Run Dashboard管理),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
使用Springboot實(shí)現(xiàn)OAuth服務(wù)的示例詳解
OAuth(Open Authorization)是一個(gè)開放標(biāo)準(zhǔn),用于授權(quán)第三方應(yīng)用程序訪問用戶資源,而不需要共享用戶憑證。本文主要介紹了如何使用Springboot實(shí)現(xiàn)一個(gè)OAuth服務(wù),需要的可以參考一下2023-05-05

