1. 개요
Map
자료구조를 사용하는 경우가 많은데, 항상 정렬하는 부분에서 까먹고 구글링을 하게 된다. 답답해서 내 블로그에 정리한다.
또 저번에 문제 풀다가 key 하나에 value여러개를 저장해야하는 경우가 있었는데, Value를 List
로 두어 구현하니 1 key, multi value가 가능했다. 이것도 까먹을 수 있으니 정리해두자.
2. Map 정렬
Map
자료구조를 정렬할 때 크게 두 가지 경우를 생각해 볼 수 있다.
- Key를 기준으로 정렬
- Value 기준으로 정렬
2-1. Key를 기준으로 정렬
Key를 기준으로 정렬할 때 TreeMap
을 사용하면 된다. TreeMap
은 저장할 때 Key 순서로 저장하도록 구현되어있다. 오름차순 정렬, 내림차순 정렬, 또는 다른 기준으로 정렬할 수 있는데 이 때 Comparator
를 사용한다.
예시를 보자
package algorithm.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.junit.jupiter.api.Test;
public class MapSortTest {
Map<Integer, String> numberCountryMap;
MapSortTest() {
numberCountryMap = new HashMap<>();
numberCountryMap.put(82, "대한민국");
numberCountryMap.put(1, "미국");
numberCountryMap.put(33, "프랑스");
numberCountryMap.put(7, "러시아");
numberCountryMap.put(61, "호주");
numberCountryMap.put(84, "베트남");
numberCountryMap.put(81, "일본");
numberCountryMap.put(886, "대만");
numberCountryMap.put(44, "영국");
}
@Test
public void mapEntireSortByAscendingKeyTest() {
Map<Integer, String> keyAscendingMap = new TreeMap<>(Comparator.naturalOrder());
//Map<Integer, String> keyAscendingMap = new TreeMap<>((o1, o2) -> o1.compareTo(o2));도 가능
keyAscendingMap.putAll(numberCountryMap);
System.out.println("---key ascending map ---");
printMap(keyAscendingMap);
}
}
생성자에서 구현한 numberCountryMap
을 출력하면 저장한 순서대로 출력된다. 그러나 TreeMap
으로 구현한 keyAscendingMap
은 key 오름차순 순서로 출력된다. 생성자에 원하는 정렬 기준 Comparator
를 입력으로 하면 된다.
이번엔 내림차순으로 정렬한 것을 보자
package algorithm.map;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.junit.jupiter.api.Test;
public class MapSortTest {
//numberCountryMap은 오름차순 때와 같음
@Test
public void mapEntireSortByDescendingKeyTest() {
Map<Integer, String> keyDescendingMap = new TreeMap<>(Comparator.reverseOrder());
// Map<Integer, String> keyDescendingMap = new TreeMap<>((o1, o2) -> o2.compareTo(o1));도 가능
keyDescendingMap.putAll(numberCountryMap);
System.out.println("---key descending map---");
printMap(keyDescendingMap);
}
}
정렬 기준을 익명클래스로 Comparator를 생성하거나, 람다식을 이용해서 구현할 수도 있다.
2-2. Value를 기준으로 정렬
Key를 기준으로 한 Map
의 정렬은 TreeMap
을 이용해서 쉽게 구현할 수 있다. 그렇다면 Value를 기준으로 한 정렬은 어떻게 구현할까?
Map.Entry
를 List
로 저장하여 value를 기준으로 정렬 한 후 LinkedHashMap
에 저장한다.
예시로 쉽게 알아보자.
package algorithm.map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.junit.jupiter.api.Test;
public class MapSortTest {
// numberCountryMap은 위 예시들과 같음
@Test
public void mapEntireSortByAscendingValueTest() {
List<Entry<Integer, String>> entryList = new ArrayList<>(numberCountryMap.entrySet());
entryList.sort((o1, o2) -> o1.getValue().compareTo(o2.getValue()));
// entryList.sort(Comparator.comparing(Entry::getValue));도 가능
// entryList.sort(Entry.comparingByValue());도 가능
Map<Integer, String> valueAscendingMap = new LinkedHashMap<>();
for (Entry<Integer, String> entry : entryList) {
valueAscendingMap.put(entry.getKey(), entry.getValue());
}
System.out.println("--- value ascending map ---");
printMap(valueAscendingMap);
}
@Test
public void mapEntireSortByDescendingValueTest() {
List<Entry<Integer, String>> entryList = new ArrayList<>(numberCountryMap.entrySet());
entryList.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
Map<Integer, String> valueDescendingMap = new LinkedHashMap<>();
for (Entry<Integer, String> entry : entryList) {
valueDescendingMap.put(entry.getKey(), entry.getValue());
}
System.out.println("--- value descending map ---");
printMap(valueDescendingMap);
}
}
예시에서는 List
를 정렬 후 LinkedHashMap
에 저장했지만, 정렬 된Map
을 구현하는 것이 아닌, 단순 출력 목적이라면 List
까지만 구현해도 된다.
2-3. 번외 - Key만 필요하거나, Value만 필요할 경우
그냥 Entry
로 List
를 구현하여 정렬하는것이 더 낫다고 생각하지만, 참고용으로 keySet()
과 values()
로 구현한것도 정리해둔다.
package algorithm.map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.junit.jupiter.api.Test;
public class MapSortTest {
// numberCountryMap은 위 예시들과 같음
@Test
public void keyListTest() {
List<Integer> keys = new ArrayList<>(numberCountryMap.keySet());
for (Integer key : keys) {
System.out.println("Key : " + key);
}
System.out.println();
}
@Test
public void valueListTest() {
List<String> values = new ArrayList<>(numberCountryMap.values());
for (String value : values) {
System.out.println("Value : " + value);
}
System.out.println();
}
}
3. one Key Multi Value
Spring에 MultiValueMap
이 있기는 하지만 사용하기 위해선 라이브러리를 추가해주어야 한다.
Spring 라이브러리 추가 없이 사용하기 위해 Map<Key, Value>
에서 Value를 List
로 사용한다.
package algorithm.map;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.junit.jupiter.api.Test;
public class MultiValueMapTest {
Map<Character, List<String>> dictionaryMap = new TreeMap<>();
@Test
public void multiValueMapTest() {
addWord("apple");
addWord("dig");
addWord("drug");
addWord("banana");
addWord("abc-Mart");
addWord("africa");
addWord("ace");
addWord("big");
addWord("boy");
addWord("drum");
addWord("cap");
printSortedDictionaryMap(dictionaryMap);
}
private void printSortedDictionaryMap(Map<Character, List<String>> dictionaryMap) {
for (Entry<Character, List<String>> entry : dictionaryMap.entrySet()) {
System.out.println("--- " + entry.getKey() + " ---");
List<String> words = entry.getValue();
words.sort(Comparator.naturalOrder());
for (int i = 0; i < words.size(); i++) {
System.out.println(i + 1 + ". " + words.get(i));
}
}
}
private void addWord(String word) {
List<String> words = dictionaryMap.containsKey(word.charAt(0))
? dictionaryMap.get(word.charAt(0))
: new ArrayList<>();
words.add(word);
dictionaryMap.put(word.charAt(0), words);
}
}
addWord()
에서 Map
에 key가 없다면 새 List
를 생성하고, key가 있다면 key에 해당하는 List
를 가져온다. 그리고 List
에 word를 저장하고 List
를 Map
에 저장한다.
printSortedDictionaryMap()
에서 반복문으로 Entry
를 가져와서 Key와 Value를 가져온다. Value는 List
이므로 정렬 후 List
를 돌며 word를 출력한다.
위의 정렬 예시와 마찬가지로 정렬된 Map
으로 저장하고 싶다면 LinkedHashMap<Character, List<String>>
으로 새 Map
을 생성하여 정렬된 List
를 순서대로 저장한다.
'Java' 카테고리의 다른 글
[IntelliJ] JUnit Test 한글 깨짐 해결 (0) | 2024.02.03 |
---|---|
[IntelliJ] no matching in any candidates test task 해결법 (0) | 2024.02.03 |
[Spring Boot] Thymeleaf properties 설정 (0) | 2023.12.27 |
[Java] Time 패키지 (LocalTime, LocalDate, LocalDateTime) (0) | 2020.11.12 |
[Java] Date, Calendar 클래스 (0) | 2020.10.31 |