Developet/etc

[Java] Map 사용법(1) - Map 정렬과, MultiValueMap

KSerin 2021. 11. 10. 15:57
728x90

출처 : https://jino-dev-diary.tistory.com/entry/Java-Map-%EC%82%AC%EC%9A%A9%EB%B2%951-Map-%EC%A0%95%EB%A0%AC%EA%B3%BC-MultiValueMap

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를 입력으로 하면 된다.

기본 맵

key 오름차순 정렬

 

이번엔 내림차순으로 정렬한 것을 보자

 

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를 생성하거나, 람다식을 이용해서 구현할 수도 있다.

key 내림차순 정렬

2-2. Value를 기준으로 정렬

Key를 기준으로 한 Map의 정렬은 TreeMap을 이용해서 쉽게 구현할 수 있다. 그렇다면 Value를 기준으로 한 정렬은 어떻게 구현할까?

Map.EntryList로 저장하여 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까지만 구현해도 된다.

value 오름차순(가나다 순) 정렬

value 내림차순 정렬

2-3. 번외 - Key만 필요하거나, Value만 필요할 경우

그냥 EntryList를 구현하여 정렬하는것이 더 낫다고 생각하지만, 참고용으로 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();

  }

 

}

 

key list

value list


3. one Key Multi Value

Spring에 MultiValueMap이 있기는 하지만 사용하기 위해선 라이브러리를 추가해주어야 한다.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/MultiValueMap.html

 

MultiValueMap (Spring Framework 5.3.1 API)

default void addIfAbsent(K key, V value) Add the given value, only when the map does not contain the given key.

docs.spring.io

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를 저장하고 ListMap에 저장한다.

printSortedDictionaryMap()에서 반복문으로 Entry를 가져와서 Key와 Value를 가져온다. Value는 List이므로 정렬 후 List를 돌며 word를 출력한다.

위의 정렬 예시와 마찬가지로 정렬된 Map으로 저장하고 싶다면 LinkedHashMap<Character, List<String>>으로 새 Map을 생성하여 정렬된 List를 순서대로 저장한다.

 

728x90

'Developet > etc' 카테고리의 다른 글

python 설치(windows)  (0) 2022.02.03
git for windows  (0) 2022.02.03
aws ec2 접속 계정  (0) 2022.01.24
SW기술자 평균임금  (0) 2021.11.17
nginX + 리버스프록시 + https(ssl) 적용하기  (0) 2021.11.09