ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] HashMap을 정렬하는 방법
    Language/Java 2020. 4. 8. 23:37
    728x90
    반응형

    Map의 구조처럼 Key, Value의 형태를 정렬을 할 때는 Comparable or Comparator 인터페이스를 구현한 후에 해당 인터페이스에 맞는 compare or compareTo 메소드를 오버라이딩 해서 정렬의 기준을 재정의 한 후에 하면 된다는 것은 알았지만 자세히 사용법을 정리해보려 한다. Key를 기준으로 정렬을 하고 싶다는 그냥 TreeMap을 이용하면 된다. 

    하지만 Value를 기준으로 정렬하고 싶다면 어떻게 해야할까? 

     

     

     

     

     

    1. Value를 기준으로 Map을 정렬하기 

    Value를 기준으로 정렬하고 싶다면 위에서 말한 것처럼 Comparator 인터페이스를 implements 한 후에 compare 메소드를 오버라이딩 하여야 한다. 

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import java.util.*;
     
    public class CompareTest {
        public static void main(String[] args){
            Map<String, Integer> map = new HashMap<>();
            map.put("A"4);
            map.put("B"4);
            map.put("C"4);
            map.put("D"2);
            map.put("E"6);
            map.put("F"1);
            map.put("G"2);
     
            // value 내림차순으로 정렬하고, value가 같으면 key 오름차순으로 정렬
            List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
     
            Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                    if (o1.getValue() > o2.getValue()) {
                        return -1;
                    }
                    else if (o1.getValue() < o2.getValue()) {
                        return 1;
                    }
     
                    return o1.getKey().compareTo(o2.getKey());
                }
            });
     
            // 순서유지를 위해 LinkedHashMap을 사용
            Map<String, Integer> sortedMap = new LinkedHashMap<>();
            for(Iterator<Map.Entry<String, Integer>> iter = list.iterator(); iter.hasNext();){
                Map.Entry<String, Integer> entry = iter.next();
                sortedMap.put(entry.getKey(), entry.getValue());
            }
     
            System.out.println(sortedMap);
        }
    }
     
     
     

    결과창 

     
    {E=6, A=4, B=4, C=4, D=2, G=2, F=1}
     

     

    일단 HashMap은 순서가 존재하지 않기 때문에 List에 먼저 담아야 한다. 하지만 우리는 <Key, Value> 쌍을 유지한 상태로 정렬을 하는 것을 원하기 때문에 Map.Entry라는 것을 이용한다.

    1
     List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
     

     

     

     

    그러면 Map.Entry는 무엇일까? 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    interface Entry<K,V> {
           
            K getKey();  
            V getValue();
            V setValue(V value);
            boolean equals(Object o);
            int hashCode();
           
            public static <extends Comparable<super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
                return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> c1.getKey().compareTo(c2.getKey());
            }
        
            public static <K, V extends Comparable<super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
                return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> c1.getValue().compareTo(c2.getValue());
            }
     
            public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<super K> cmp) {
                Objects.requireNonNull(cmp);
                return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
            }
     
            public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<super V> cmp) {
                Objects.requireNonNull(cmp);
                return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
            }
        }
     
     

     

    • Map 인터페이스의 내부 인터페이스이다.
    • Map에 저장되는 <Key, Value)쌍을 다루기 위해 내부적으로 Entry 인터페이스를 정의해놓았다.
    • Map인터페이스를 구현하는 클래스에서는 Map.Entry인터페이스도 함께 구현해야 한다.

     

    1
    List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
     

    그러면 이렇게 생성자에다가 map.entrySet()을 넣었다. 그러면 entrySet은 HashMap 클래스의 메소드이다.  

     

    HashMap 클래스 내부

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;
            final K key;
            V value;
            Node<K,V> next;
     
            Node(int hash, K key, V value, Node<K,V> next) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = next;
            }
     
            public final K getKey()        { return key; }
            public final V getValue()      { return value; }
            public final String toString() { return key + "=" + value; }
     
            public final int hashCode() {
                return Objects.hashCode(key) ^ Objects.hashCode(value);
            }
     
            public final V setValue(V newValue) {
                V oldValue = value;
                value = newValue;
                return oldValue;
            }
     
            public final boolean equals(Object o) {
                if (o == this)
                    return true;
                if (o instanceof Map.Entry) {
                    Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                    if (Objects.equals(key, e.getKey()) &&
                        Objects.equals(value, e.getValue()))
                        return true;
                }
                return false;
            }
        }
     
     

    일부분을 가져왔는데, 보면 HashMap 클래스에서 Map인터페이스를 구현했고 추가로 Map.Entry인터페이스를 구현한 것을 알 수있다. 

     

    1
    2
    3
    4
    public Set<Map.Entry<K,V>> entrySet() {
            Set<Map.Entry<K,V>> es;
            return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
    }
     
     

    그리고 HashMap 클래스의 entrySet메소드를 보면 위와 같이 구현이 되어있다. 반환형이 Set<Map.Entry<K,V>이다.

     

            Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                    if (o1.getValue() > o2.getValue()) {
                        return -1;
                    }
                    else if (o1.getValue() < o2.getValue()) {
                        return 1;
                    }
     
                    return o1.getKey().compareTo(o2.getKey());
                }
            });
     
     
     
     

     

    그리고 이와 같이 compare 메소드의 매게변수를 보면 Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2임을 알 수 있는데 이 때 호출하는 곳에서 인자로 들어오는 게 List에 담은 Map.Entry의 형태이기 때문에 사용이 가능한 것이다. 

    반응형

    댓글

Designed by Tistory.