如何對配對清單進行排序<String, Integer>
1. 概述
在 Java 中,我們經常需要成對地處理數據,Apache Commons Lang3 函式庫為此目的提供了一個方便的Pair
類別。當我們有一個Pair<String, Integer>
列表時,很多情況下我們需要按整數值對其進行排序。
在本教程中,我們將探索以整數部分對 Apache Commons Lang3 的Pair<String, Integer>
List
進行排序的各種方法。
2.問題介紹
Apache Commons Lang3 中的Pair
類別提供了一個簡單的結構來保存兩個值。由於它是 Java 中常用的Pair
類型之一,因此我們將在本教程中使用它作為範例。
像往常一樣,讓我們透過範例來理解問題。我們先建立一個方法來建構 Apache Commons Lang3 的Pair<String, Integer>
物件List
:
private List<Pair<String, Integer>> getUnsortedInput() {
return Arrays.asList(
Pair.of("False", 5),
Pair.of("Yes", 3),
Pair.of("True", 4),
Pair.of("No", 2),
Pair.of("X", 1)
);
}
如上面的程式碼所示, getUnsortedInput()
方法產生一個未排序的List<Pair<String, Integer>>.
每個**Pair**
元素包含兩個值:一個**String**
和它所包含的字母的Integer
計數。
我們的目標是依照每個Pair
元素的整數值來對List
進行排序。因此,預期結果是:
static final List<Pair<String, Integer>> EXPECTED = List.of(
Pair.of("X", 1),
Pair.of("No", 2),
Pair.of("Yes", 3),
Pair.of("True", 4),
Pair.of("False", 5)
);
接下來,我們將探索解決這個有趣的排序問題的不同方法。
3. 使用匿名Comparator
類
我們知道我們需要比較元素來對資料集合進行排序。 Apache Commons Lang3 的Pair
類別沒有實作Comparable
介面。因此,我們不能直接比較兩個Pair
物件。
但是,我們可以建立一個實作Comparator
介面的匿名類別來比較兩個Pair
的整數值,並將Comparator
傳遞給List.sort()
:
List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort(new Comparator<Pair<String, Integer>>() {
@Override
public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
return o1.getRight()
.compareTo(o2.getRight());
}
});
assertEquals(EXPECTED, myList);
如上面的程式碼所示,匿名Comparator
類別實作了compare()
方法。由於Integer
實作了Comparable
,因此我們使用Integer.compareTo()
來比較兩個Pair
元素的整數值。
值得一提的是,Apache Commons Lang3 的Pair
類別提供了getRight()
和getValue()
方法來傳回Pair
物件中的第二個值。這兩種方法沒有差別。實際上, getValue()
呼叫了getRight()
方法:
public abstract R getRight();
public R getValue() {
return this.getRight();
}
當我們運行測試時,它通過了。因此,這個方法解決了這個問題。
4. 使用 Lambda 表達式
匿名Comparator
類別方法解決了我們的排序問題。然而,匿名類別代碼並不容易閱讀。從 Java 8 開始, List.sort()
方法提供了一個功能可能性:lambda 表達式作為Comparator
支援。
接下來,讓我們將匿名Comparator
類別解決方案重構為與 lambda 表達式進行比較:
List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort((p1, p2) -> p1.getRight()
.compareTo(p2.getRight()));
assertEquals(EXPECTED, myList);
正如我們所看到的,lambda 表達式方法提供了一種實作Comparator
介面的簡潔方法。它使程式碼更具可讀性並且更易於維護。
5. 使用Comparator.comparing()
從Java 8開始, Comparator
介面提供了一個新的comparing()
方法。此方法接受Function
keyExtractor
並傳回按下該排序鍵進行比較的Comparator
。
接下來,讓我們使用Comparator.comparing()
方法來解決排序問題:
List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort(Comparator.comparing(Pair::getRight));
assertEquals(EXPECTED, myList);
在此範例中,我們將方法引用Pair::getRight
作為函數參數傳遞給comparing()
方法。它會建立一個Comparator
對象,該物件會以整數值對給定List<Pair<String, Integer>>
中的Pair
元素進行排序。
6. 排序結果的新List
我們已經看到了排序問題的三種解決方案。值得注意的是,所有三種方法都執行就地排序,這會更改原始輸入List.
但是,有時我們無法修改輸入清單 - 例如,如果我們的輸入是不可變的List
:
List<Pair<String, Integer>> immutableUnsortedList = List.copyOf(getUnsortedInput());
assertThrows(UnsupportedOperationException.class,
() -> immutableUnsortedList.sort(Comparator.comparing(Pair::getRight)));
在這個範例中,我們使用List.copyOf()
來建立一個不可變的List,
當我們使用先前的解決方案對其進行排序時會拋出例外。
因此,我們必須將排序結果作為新的**List**
物件來取得。
實現此目的的一個簡單想法是首先複製原始List
and
然後對複製的List
進行排序。
或者,我們可以使用Stream
的sorted()
方法來執行排序,然後將排序後的元素收集到一個新的List
物件中。讓我們看看實際效果:
List<Pair<String, Integer>> immutableUnsortedList = List.copyOf(getUnsortedInput());
List<Pair<String, Integer>> sorted = immutableUnsortedList.stream()
.sorted(Comparator.comparing(Pair::getRight))
.toList();
assertEquals(EXPECTED, sorted);
正如我們所看到的, stream().sorted().toList()
管道易於閱讀並流暢地解決了問題。
七、結論
在本文中,我們探討了根據每個Pair
的整數值對Pair<String, Integer>
元素List
進行排序的各種方法。此外,我們也討論如何為排序結果取得新的List
對象,並保持原始List
不變。
與往常一樣,範例的完整原始程式碼可在 GitHub 上取得。