Stream API を使用して Map をソートする


概要

Java にて Map に格納されている Entry を Value (降順) 、Key (昇順) の順番でソートしたいと考えました。

簡単にソートしたいので、Stream API を使用することを考えましたが、使用方法について少しはまってしまったので自分用に備忘録を残したいと思います。

ソート前

key
value

21
10

11
12

2
10

ソート後

key
value

11
12

2
10

21
10


実装


Sort.java


import java.util.*;
import java.util.stream.Collectors;

public class Sort {

public static void main(String[] args) {
// 挿入順は担保されないが上記図のイメージに合わせて挿入
Map<Integer, Integer> map = new HashMap<>();
map.put(21, 10);
map.put(11, 12);
map.put(2, 10);
Map<Integer, Integer> result = map.entrySet().stream()
.sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed()
.thenComparing(Map.Entry.comparingByKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

for (Map.Entry<Integer, Integer> sorted : result.entrySet()) {
System.out.printf("Key is %d value is %d \n", sorted.getKey(), sorted.getValue());
}
}
}



結果

Key is 11  value is 12 

Key is 2 value is 10
Key is 21 value is 10


補足

HashMap では挿入順序が担保されないので、LinkedHashMap を使用しています。


HashMap

Mapインタフェースのハッシュ表に基づく実装です。この実装は、マップに関連するオプションのオペレーションをすべてサポートし、null値およびnullキーを使用できます。HashMapクラスはHashtableと同じとみなしてもかまいませんが、HashMapの方は同期がとられず、nullの場合もあります。このクラスはマップの順序を保証しません。特に、その順序を常に一定に保つことを保証しません。

LinkedHashMap

この実装では、HashMapおよびHashtableで提供される未指定(無秩序)の順序がクライアントで起きることはありません(TreeMapのように負荷が増えることもありません)。この実装を使用することで、元のマップの実装にかかわらず、元のマップと同じ順序のコピーを作成できます。



参考

こちらのページ から学習させていただきました。

toMap の引数は、下記となっています。

N
引数名
内容

1
keyMapper
新規Map のため Key を生成するための関数

2
valueMapper
新規Map のため value を生成するための関数

3
mergeFunction
新規キー (例では key になる)

4
mapSupplier
新規Mapインスタンスを生成する関数


Collectors.java


* @param <T> the type of the input elements
* @param <K> the output type of the key mapping function
* @param <U> the output type of the value mapping function
* @param <M> the type of the resulting {@code Map}
* @param keyMapper a mapping function to produce keys
* @param valueMapper a mapping function to produce values
* @param mergeFunction a merge function, used to resolve collisions between
* values associated with the same key, as supplied
* to {@link Map#merge(Object, Object, BiFunction)}
* @param mapSupplier a function which returns a new, empty {@code Map} into
* which the results will be inserted
* @return a {@code Collector} which collects elements into a {@code Map}
* whose keys are the result of applying a key mapping function to the input
* elements, and whose values are the result of applying a value mapping
* function to all input elements equal to the key and combining them
* using the merge function
*
* @see #toMap(Function, Function)
* @see #toMap(Function, Function, BinaryOperator)
* @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier)
*/
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {

}