如何在 Java 中按值對 LinkedHashMap 進行排序
1. 概述
當我們需要管理鍵值關聯時, Map
是一種常見的資料類型。 LinkedHashMap
是一種流行的選擇,主要以保留插入順序而聞名。然而,在許多現實場景中,我們經常需要根據值而不是鍵對LinkedHashMap
的元素進行排序。
在本教程中,我們將探討如何在 Java 中按值對LinkedHashMap
進行排序。
2. 按值排序
LinkedHashMap
的預設行為是根據插入順序維護元素的順序。當我們想要追蹤元素添加到地圖的順序時,這非常有用。但是,按值排序是不同的要求。我們可能希望根據與鍵關聯的值按升序或降序排列條目。
接下來我們來看一個例子。假設我們有一個名為 MY_MAP 的LinkedHashMap
MY_MAP:
static LinkedHashMap<String, Integer> MY_MAP = new LinkedHashMap<>();
static {
MY_MAP.put("key a", 4);
MY_MAP.put("key b", 1);
MY_MAP.put("key c", 3);
MY_MAP.put("key d", 2);
MY_MAP.put("key e", 5);
}
如上面的範例所示,我們使用static
區塊初始化MY_MAP
。地圖中的數值是整數。我們的目標是按值對映射進行排序,並獲得一個等於EXPECTED_MY_MAP
的新LinkedHashMap
:
static LinkedHashMap<String, Integer> EXPECTED_MY_MAP = new LinkedHashMap<>();
static{
EXPECTED_MY_MAP.put("key b", 1);
EXPECTED_MY_MAP.put("key d", 2);
EXPECTED_MY_MAP.put("key c", 3);
EXPECTED_MY_MAP.put("key a", 4);
EXPECTED_MY_MAP.put("key e", 5);
}
接下來,我們將看到解決該問題的幾種方法。我們將使用單元測試斷言來驗證每個解決方案。
3.使用Collections.sort()
方法
首先,讓我們看看如果我們的Java版本早於Java 8,如何解決這個問題。
LinkedHashMap's entrySet()
提供對所有條目的訪問,同時保持其原始順序。
我們也可以利用Collections.sort()
方法,它允許我們透過給定的Comparator.
我們先看一下解決方案:
List<Map.Entry<String, Integer>> entryList = new ArrayList<>(MY_MAP.entrySet());
Collections.sort(entryList, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
for (Map.Entry<String, Integer> e : entryList) {
result.put(e.getKey(), e.getValue());
}
assertEquals(EXPECTED_MY_MAP, result);
讓我們快速瀏覽一下程式碼以了解其工作原理。
首先,我們將entrySet()'s
結果包裝在一個List.
然後,我們建立了一個匿名Comparator
來按條目的值對條目進行排序並將其傳遞給Collections.sort()
方法。最後,我們建立一個新的LinkedHashMap
物件並將排序後的條目放入其中。
4. 使用forEachOrdered()
Stream API是Java 8帶給我們的一個重要的新功能。它使我們能夠方便地操作集合。因此,如果我們使用的 Java 版本是 8 或更高版本,我們可以使用 Stream API 以原始映射中的排序條目填入一個空的LinkedHashMap
:
LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
MY_MAP.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.forEachOrdered(entry -> result.put(entry.getKey(), entry.getValue()));
assertEquals(EXPECTED_MY_MAP, result);
正如我們所看到的,使用 Stream API,解決方案更加流暢和緊湊。
值得注意的是, Map.Entry
支援comparingByValue()
方法。顧名思義,它會傳回一個Comparator
,用於按條目的值進行比較。
由於我們範例中的Entry.value
是Integer
,它是Comparable,
我們可以直接呼叫comparingByValue()
。
5. 使用collect()
另一種更簡化的方法涉及利用collect()
方法一次建立映射並累積排序的條目:
LinkedHashMap<String, Integer> result = MY_MAP.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), Map::putAll);
assertEquals(EXPECTED_MY_MAP, result);
collect()
方法是這種方法的關鍵。它需要三個參數:
- Supply (
LinkedHashMap::new)
– 提供一個新的容器 (LinkedHashMap
) 來累積結果 - Accumulator (
(map, entry) -> map.put(entry.getKey(), entry.getValue())
– 此函數應用於流中的每個元素,並將每個條目加入累積的LinkedHashMap
中 - 組合器 (
Map::putAll
) – 在平行處理中,它組合由多個累加器更新的容器。在這種情況下,這是無關緊要的,因為流是按順序處理的。
因此, collect()
將排序後的條目累積到一個新的LinkedHashMap.
6. 當值不Comparable
時
我們已經了解如何按值對MY_MAP
進行排序。由於Integer
值是Comparable,
當我們使用Stream API時,我們可以簡單地呼叫sorted(Map.Entry.comparingByValue()).
但是,如果該值不是Comparable
,我們需要將Comparator
傳遞給comparingByValue()
:
class Player {
private String name;
private Integer score = 0;
public Player(String name, Integer score) {
this.name = name;
this.score = score;
}
// ... hashcode, equals, getters methods are omitted ...
}
如程式碼所示, Player
類別沒有實作**Comparable** .
現在,讓我們初始化一個LinkedHashMap<String, Player>
:
static LinkedHashMap<String, Player> PLAYERS = new LinkedHashMap<>();
static {
PLAYERS.put("player a", new Player("Eric", 9));
PLAYERS.put("player b", new Player("Kai", 7));
PLAYERS.put("player c", new Player("Amanda", 20));
PLAYERS.put("player d", new Player("Kevin", 4));
}
假設我們想根據玩家的得分對玩家進行PLAYERS
並獲得一個新的LinkedHashMap **:**
static LinkedHashMap<String, Player> EXPECTED_PLAYERS = new LinkedHashMap<>();
static {
EXPECTED_PLAYERS.put("player d", new Player("Kevin", 4));
EXPECTED_PLAYERS.put("player b", new Player("Kai", 7));
EXPECTED_PLAYERS.put("player a", new Player("Eric", 9));
EXPECTED_PLAYERS.put("player c", new Player("Amanda", 20));
}
接下來,讓我們看看如何實現這一目標:
LinkedHashMap<String, Player> result = PLAYERS.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue(Comparator.comparing(Player::getScore)))
.collect(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), Map::putAll);
assertEquals(EXPECTED_PLAYERS, result);
在本例中,我們在comparingByValue()
方法中使用了Comparator.comparing(Player::getScore)
。
此建構透過實例方法引用產生一個Comparator
,專門比較Player
物件的score
欄位。
七、結論
在本教程中,我們探索了按值對LinkedHashMap
進行排序的不同方法。我們也解決了值未實作Comparable
介面的情況下的排序實作。
與往常一樣,範例的完整原始程式碼可在 GitHub 上取得。