본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Java(자바)

Java Map 정렬하기(자바 맵 정렬)

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2020. 7. 6.

Java버전이 8이상으로 올라오면서 컬렉션에 대한 자료처리하는 다양한 함수들이 생겨났습니다.

힘들게 만들었던 알고리즘과 각종 메소드를 통해 하였던 작업이 이제는 손쉽게 만들 수 있게 되었습니다!

이번에 살펴볼 기능은 Map 객체에 대한 정렬 기능입니다.

먼저 아래와 같은 Map이 있다고 가정하여  봅니다.

Map<String, Integer> my_arg = new HashMap<>();
my_arg.put("c", 10);
my_arg.put("a", 17);
my_arg.put("b", 6);    

 

위 데이터에서 key 값 기준으로 그리고 value 값 기준으로 정렬을 해 보겠습니다.

먼저 정렬을 하려면 Map 객체에서 entrySet이라는 메소드를 호출하여 스트림 객체를 만들 수 있게 해 주어야 합니다.

private static void mapSortKeyStringAndInt(Map<String, Integer> item){
    Stream<Entry<String, Integer>> stream = item.entrySet().stream();
}

 

Entry 라는 인터페이스는 Map에 대한 정보를 key와 value를 담는 객체 입니다.

여기에 자동완성을 누르면 아래처럼 메소드가 완성이 됩니다.

private static void mapSortKeyStringAndInt(Map<String, Integer> item){
    Stream<Entry<String, Integer>> stream = item.entrySet().stream();
    stream.sorted().forEach(System.out::println);
}

 

왠지모를 정렬이 다 된 기분에 해당 메소드를 실행하면 오류가 발생합니다.

Comparable을 상속받지 않아서 생긴 오류입니다.

 

sort라는 메소드를 동작시키기 위해서는 데이터가 Comparable 클래스를 상속 받고있어야 합니다.

그렇지 않을 경우 데이터를 sort하는 경우 위 에러메시지를 만나게 됩니다.

sorted메소드는 2가지 타입이 있습니다.

파라미터가 존재하는 경우, 존재하지 않는 경우 등 2가지 메소드가 있습니다.

 

위 메소드에서 stream에 담긴 값은 Entry<String, Integer> 객체이므로 Comparable를 상속받지 않고 있습니다.

그러므로 sorted 메소드의 2번째 형테인 Comparator 객체를 파라미터로 받는 모양으로 만들어 주도록 합니다.

private static void mapSortKeyStringAndInt(Map<String, Integer> item){
    Map<?, ?> result = null;
    //item.entrySet().stream().sorted().forEach(System.out::println); //HashMap에는 Comparable 객체를 상속하지 않았으므로 오류발생.
    Comparator<Map.Entry<String, Integer>> order = Map.Entry.comparingByValue();  //제네릭의 타입을 결정하면 다른 종류의 클래스 정렬도 가능 합니다.
    result = item.entrySet().stream()
      .sorted(order)
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
    System.out.println(result);
}

 

갑자기 코드가 엄청 늘어났습니다.

먼저 Entry 인터페이스가 제공하는 comparingByValue라는 메소드를 통해서 Comparator 객체를 만들어 줍니다.

그리고 중간에 보이는 sorted 메소드에 정렬 기준값으로 넣어주고 정렬된 객체를 스트림에서 제공하는 collect 메소드를 통해서 정렬하여 줍니다.

collect 내부의 Collectors.toMap이라는 메소드가 핵심이라 볼 수 있습니다.

toMap이라는 메소드는 2개 ~ 4개의 파라미터를 받습니다.

/***
  keyMapper a mapping function to produce keys
  valueMapper a mapping function to produce values
  mergeFunction a merge function, used to resolve collisions betweenvalues associated with the same key, as suppliedto Map.merge(Object, Object, BiFunction)
  mapSupplier a function which returns a new, empty Map intowhich the results will be inserted
***/

 

키 값을 매핑할 함수, 값을 매핑할 함수, 병합할 함수 및 적용형태 등 총 4가지 입니다.

오버로딩의 형태로 되어있으므로 자세한 내용은 자바doc를 참조하시면 될 것 같습니다.

이제 그러면 조금더 세련된 형태의 메소드로 바꾸어 보겠습니다.

private static void mapSortKeyStringAndInt(Map<String, Integer> item, String type){
    Map<?, ?> result = null;
    //item.entrySet().stream().sorted().forEach(System.out::println); //HashMap에는 Comparable 객체를 상속하지 않았으므로 오류발생.
    Comparator<Map.Entry<String, Integer>> order = Map.Entry.comparingByValue();  //제네릭의 타입을 결정하면 다른 종류의 클래스 정렬도 가능 합니다.
    if(type.equals("key")){
        order = Map.Entry.comparingByKey();  // * 오름차순(Comparator객체를 옆 메소드의 파라미터로 넣어주세요) : Comparator.reverseOrder()
    }
    result = item.entrySet().stream()
    .sorted(order)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
    System.out.println(result);
}

 

type이라는 변수를 한개 더 받아서 key로 정렬할지, value로 정렬할지에 대해 선택 할 수 있게 하였습니다.

조건에 따라서 데이터가 잘 정렬이 됩니다!

 

다음으로 살펴볼 내용은 사용자가 임의로 만든 vo 객체에 대해서 정렬하는 방법 입니다.

먼저 사용할 vo 클래스를 만들어 보겠습니다.

public static class ValueClass {
    private String text;
    private int number;	
    public ValueClass(){

    }
    public ValueClass(String arg, int num){
        this.text = arg;
        this.number = num;
    }		
    public String getText() {return text;}
    public void setText(String text) {this.text = text;}
    public int getNumber() {return number;}
    public void setNumber(int number) {this.number = number;}
    @Override
    public String toString() {
        return "text : "+text + ",  number : " + number;
    }
}

 

너무나 직관적이고 쉬운 코드 입니다.

위 클래스의 text 필드값을 기준으로 정렬하는 클래스를 만들어 보겠습니다.

private static void mapSortVo(Map<String, ValueClass> item){
    Map<?, ?> result = null;
    Comparator<Map.Entry<String, ValueClass>> order = Entry.comparingByValue(Comparator.comparing(ValueClass::getText));
    result = item.entrySet().stream()
    .sorted(order)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
    System.out.println(result);
}	

 

거의 비슷한 내용입니다. Comparator 객체를 만드는 부분만 조금 다를 뿐 기타 내용은 동일 합니다.

값을 기준으로 정렬되었습니다.

 

데이터가 훌륭하게 정렬되어 출력되었습니다.

key값은 일반 String 형태로 되어있습니다. key 값 기준으로 정렬하려면 위에 작성한 mapSortKeyStringAndInt 메소드를 참조하여주세요.

key값을  vo 형태로 적용하려 한 다면 mapSortVo 메소드의 값을 정렬한 방법과 동일한 방법으로 작성하면 됩니다.

다음으로 살펴볼 내용은 Object를 대상으로 하는 방법 입니다.

private static void mapSortObject(Map<Object, Object> item){
    Map<?, ?> result = null;
    Function <Object, Object> NVL = arg -> arg == null ? "" : arg;
    Function<Object, String> COMBINE = arg -> arg != null && arg instanceof String ? arg.toString() : arg+"";
    result = item.entrySet().stream().collect(Collectors.toMap(k -> NVL.andThen(COMBINE).apply(k.getKey()), e ->  NVL.andThen(COMBINE).apply(e.getValue())   ))
    .entrySet()
    .stream()
    .sorted(Map.Entry.comparingByKey())
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
    System.out.println(result);
}

 

널체크를 위해서 내부에 함수2개를 사용하였습니다.

Object형태에서 비교를 위해서 string으로 형 변환을 한 다음 정렬하게 하였습니다.

위 내용에 사용된 소스코드 모습입니다.

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class HashMapSorting {
	
    public static void main(String args[]){
        Map<String, Integer> my_arg = new HashMap<>();
        my_arg.put("c", 10);
        my_arg.put("a", 17);
        my_arg.put("b", 6);      

        mapSortKeyStringAndInt(my_arg, "key");
        System.out.println("");
        mapSortKeyStringAndInt(my_arg, "value");        

        Map<String, ValueClass> arg_vo = new HashMap<>();
        ValueClass vo = new ValueClass("eee",100);
        arg_vo.put("a", vo);
        vo = new ValueClass("ccc",200);
        arg_vo.put("b", vo);
        mapSortVo(arg_vo);

        Map<Object, Object> obj_vo = new HashMap<>();
        obj_vo.put("qqq", 121);
        obj_vo.put("aaa", "146");

        mapSortObject(obj_vo);    
    }
    
    //문자+숫자형태 정렬하기.
    private static void mapSortKeyStringAndInt(Map<String, Integer> item, String type){
        Map<?, ?> result = null;
        Comparator<Map.Entry<String, Integer>> order = Map.Entry.comparingByValue();
        if(type.equals("key")){
            order = Map.Entry.comparingByKey();  // 오름차순(Comparator객체를 옆 메소드의 파라미터로 넣어주세요) : Comparator.reverseOrder()
        }
        result = item.entrySet().stream()
        .sorted(order)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
        System.out.println(result);
    }

    //vo객체 정렬하기
    private static void mapSortVo(Map<String, ValueClass> item){
        Map<?, ?> result = null;
        Comparator<Map.Entry<String, ValueClass>> order = Entry.comparingByValue(Comparator.comparing(ValueClass::getText));
        result = item.entrySet().stream()
        .sorted(order)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
        System.out.println(result);
    }	
	
    //Object형태 정렬하기
    private static void mapSortObject(Map<Object, Object> item){
        Map<?, ?> result = null;
        Function <Object, Object> NVL = arg -> arg == null ? "" : arg;
        Function<Object, String> COMBINE = arg -> arg != null && arg instanceof String ? arg.toString() : arg+"";
        result = item.entrySet().stream().collect(Collectors.toMap(k -> NVL.andThen(COMBINE).apply(k.getKey()), e ->  NVL.andThen(COMBINE).apply(e.getValue())   ))
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByKey())
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
        System.out.println(result);
    }
	
    public static class ValueClass {
        private String text;
        private int number;	
        public ValueClass(){
        }
        public ValueClass(String arg, int num){
            this.text = arg;
            this.number = num;
        }		
        public String getText() {return text;}
        public void setText(String text) {this.text = text;}
        public int getNumber() {return number;}
        public void setNumber(int number) {this.number = number;}
        @Override
        public String toString() {
            return "text : "+text + ",  number : " + number;
        }
    }    
}    

 

추가로, 일반 문자배열과 list를 map으로 바꾸는 방법 입니다.

{
    String array[][]={
        {"key1","value1"}, {"key2","value2"}
    };
    StringArrayToMap(array);

    List<String> list = new ArrayList<>();
    list.add("aaa");
    list.add("aaa");
    list.add("bbb");
    list.add("ccc");
    listToMap(list);
}

private static void StringArrayToMap(String arg[][]){
    Map<String, String> result = Stream.of(arg).collect(Collectors.toMap( val->val[0], val->val[1]));
    System.out.println(result);
}

private static void listToMap(List<String> arg){
    Map<Object, Integer> result = arg.stream().collect(Collectors.toMap( val->val, val-> val.toString().length(), Integer::sum));
    System.out.println(result);
}

 

이상으로 Map객체에서의 정렬방법에 대해서 살펴보았습니다.

틀린부분이나 이해가되지 않는 부분에 대해서는 언제든지 연락주세요! ^^

 

* 이어서 아래글도 방문하여보세요! Map 정렬과 관련된 2차 내용입니다!

lts0606.tistory.com/432

 

Java Map 다양한 데이터 정렬하기(자바 맵 정렬 - 2)

Java에서 Map에 대한 단순한 데이터 정렬은 매우 쉽습니다. { HashMap item = new HashMap<>(); item.put("Abcd", 123); item.put("ared", 52); item.put("qred", 456); Comparator order = Comparators.comparabl..

lts0606.tistory.com

 

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글