이번 포스팅은 자바 8 스트림(Stream)을 이용해
HashMap의 키 또는 Value를 기준으로 내림차순, 오름차순 정렬을 해본다.
테스트를 위해 Map의 key,value 타입은 Map<String, Integer> 타입으로 설정 했으며,
Key는 순번, value 0~100까지의 랜덤 정수 10개를 put 했다.

먼저 map을 키 또는 값을 기준으로 정렬하기 위해서는 Map 인터페이스 내부의 Entry 인터페이스에 대해 알아야 한다.
Entry는 Map에 저장되는 key-value 쌍을 다루기 위해 Map의 내부 인터페이스로 정의되어 있다.
맵 컬렉션에 대한 자세한 설명은 아래 주소에 자세히 설명이 되어있다.
http://www.tcpschool.com/java/java_collectionFramework_map
* Map 정렬하기
Map을 Stream 객체로 만들기 위해서는 map.entrySet() 메서드를 사용해야 한다
Set<Map.Entry<String, Integer>> entries = map.entrySet();
System.out.println("map.entrySet() >>>>>>> " + map.entrySet());

Map에 entrySet() 메서드를 사용하면 Set에 담겨지게 되고 Set은 Collection이기 때문에 스트림을 사용할 수 있는 준비가 완료된다.
( Set은 모든 키값을을 하나로 뭉친 집합(Set)이라고 보면 된다. )
entries.stream().sorted().forEach(System.out::println);
이후 위에서 작성된 Set을 스트림을 사용해 일반 배열을 정렬하는 것처럼 sorted 메서드를 실행해봤다.

class java.util.HashMap$Node cannot be cast to class java.lang.Comparable 이 뜨면서 오류가 발생한다.
sort 메서드를 사용하기 위해서는 Comparable 인터페이스를 상속받아야 한다.
그러나 Map.Entry는 Comparable 를 상속받고 있지 않기 때문에 sorted의 인수로 함수형 인터페이스 Comparator를 주어야 한다.
- Map의 오름차순 정렬
Map<String, Integer> entryMap = entries.stream()
.sorted(Map.Entry.comparingByValue()) // Key를 기준으로 정렬하기 위해서는 sorted의 인자로 map.Entry.comparingByKey() 를 주면된다
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
// {key_3=5, key_1=18, key_2=19, key_6=21, key_5=39, key_8=68, key_7=77, key_4=87, key_0=88, key_9=94}
- Map의 내림차순 정렬
Map<String, Integer> entryMap = entries.stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
// {key_7=91, key_0=88, key_1=82, key_2=77, key_3=69, key_5=61, key_9=55, key_8=40, key_4=13, key_6=7}
내림차순 역시 Key를 기준으로 정렬하기 위해서는 sorted의 인자로 map.Entry.comparingByKey() 를 주면된다.
오름차순과 차이가 있다면 comparingByValue의 인자로 Comparator 함수형 인터페이스를 파라미터로 넘겨준다.
Comparator.reverseOrder()은 Comparable 인터페이스를 반환하고
Collections의 reverseOrder은 Comparator을 반환하기 때문에 인수로 넘겨줄 수 있다.


- Map의 내림차순 정렬 후 다시 Key를 기준으로 오름차순 정렬하여 List로 받기
Map<String, Integer> entryMap = entries.stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
System.out.println(entryMap);
List<Map.Entry<String, Integer>> list = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toList()); // 리스트로 반환
System.out.println(list); // Key 기준으로 정렬 각 리스트의 요소에 Map이 들어감

자세히 보면 entryMap은 중괄호 {} 로 감싸져 있고
list는 대괄호 [] 로 감사쪄 있는 것을 확인할 수 있다.
- collect(Collectors.toMap 설명)
마지막으로 Collectors.toMap에 대한 설명이다.
그냥 보기엔 이해가 어려울 수 있지만, toMap의 경우에는 3가지로 오버로딩 되어있다.
// 1번
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
// 2번
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
// 3번
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapFactory)
위 예제에서는 정렬이 완료된 스트림을 Map으로 변환시키기 위해 파라미터가 4개인 3번째 Case를 사용했다.
간단히 설명하자면
첫번째 인자 (Map.Entry::getKey) : Key를 매핑할 인자로 Map의 key를 사용한다는 의미
두번째 인자 (Map.Entry::getValue) : Value를 매핑할 인자로 Map의 value를 사용한다는 의미
세번째 인자 (oldValue, newValue) -> oldValue : BinaryOperator<U>를 타입으로 받는 mergeFunction은
동일한 Key로 인해 충돌이 발생한 경우 어떤 Value를 취할 것인지 결정할 때 사용한다.
람다의 파라미터로 oldValue와 newValue가 주어지는데 -> 로 리턴 시
oldValue를 리턴하면 처음 들어온 값을 유지할 때 사용하며
newValue를 리턴하면 이전의 값을 새로 들어온 값으로 변경할때 사용한다.
그 외 임의의 값을 리턴하면 중복되는 값이 있는 경우 임의의 값이 들어가게 된다.
마지막 네번째 인자 LinkedHashMap::new : Supplier<M> mapFactory의 경우 반환될 때 사용할 비어있는 Map을 리턴해줘야 한다.
HashMap의 경우 Map에 put이 될때 순서가 보장되지 않은 채로 put이 된다.
따라서 순서를 보장하기 위해 LinkedHashMap을 사용했다.
이 포스팅에서는 Map을 이용해 간단한 오름차순과 내림차순 정렬 그리고 toMap에 대한 사용법을 작성했다.
다음 언제가 될진 모르겠지만, 스트림과 함수형 인터페이스를 활용해 객체, Object 등
다양한 데이터 형식에 대한 정렬 및 Map으로 변환에 대해 작성할 예정이다.
'JAVA' 카테고리의 다른 글
[JAVA] 자바8 Stream Collector의 사용 방법 및 다양한 예제 (6) | 2023.09.18 |
---|---|
[JAVA] 자바8 스트림 Map.Entry를 활용해 키(key) 또는 값(value)을 기준으로 Map(맵)정렬하기 2 (0) | 2023.09.13 |
[JAVA] 자바 객체 리스트(List) 정렬하기 (오름, 내림차순) (0) | 2023.09.11 |
[JAVA] 자바8 메서드 참조 (Method reference) 이중콜론 :: (0) | 2023.09.09 |
[JAVA] Stream Collectors.groupingBy() (그룹화) 를 사용해 다양한 데이터 그룹화 하기 (1) | 2023.09.06 |