만족은 하되 안주하지는 말자

기록해야 기억한다

프로그래밍/JAVA

[Java] custom Object 리스트를 Map 리스트로 변환하기 (object to map using stream)

D36choi 2021. 4. 22. 18:04
728x90

목표

객체의 field 와 value 를 꺼내서 Map 객체에 담아 변형하자.

key는 필드변수명, value 는 해당 필드 변수의 값이다.

 

만약 [Person={id:1,name:"choi",height:180},Person={id:1,name:"kim",height:170}] 이라는 배열이 있다면,

 

[{id:1,name:"choi",height:180},{id:1,name:"kim",height:170}] 의 형태로 바꾸고 싶은 것이다.

 

샘플 클래스

public class streamPractice {

    public static class Person {
        private int id;
        private String name;
        private int height;

        private Person(int id, String name, int height) {
            this.id = id;
            this.name = name;
            this.height = height;
        }

        public int getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        public int getHeight() {
            return height;
        }

        public static Person of(int id, String name, int height) {
            return new Person(id,name,height);
        }

}

Person 이라는 객체를 정의했다. 해당 객체 안에는 id, 이름, 키를 가지고 있다.

내가 원하는건, 이 Person 리스트를 Map 리스트로 변환하는 것이다.

 

방법 1

stream.map().collect(Collectors.toList()) 의 활용



import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class streamPractice {

    public static class Person {
        private int id;
        private String name;
        private int height;

        private Person(int id, String name, int height) {
            this.id = id;
            this.name = name;
            this.height = height;
        }

        public int getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        public int getHeight() {
            return height;
        }

        public static Person of(int id, String name, int height) {
            return new Person(id,name,height);
        }
        public static Map<String, Object> toMap(Person person) {
            try {
                Field[] fields = person.getClass().getDeclaredFields();
                Map<String, Object> results = new HashMap<>();
                for (Field field : fields) {
                    results.put(field.getName(), field.get(person));
                }
                return results;

            } catch (IllegalAccessException | IllegalArgumentException e) {
                return null;
            }

        }

    }

    @Test
    public void Test() {

        final List<Person> people = Arrays.asList(Person.of(1, "choi", 170),
                                                  Person.of(2, "kim", 190),
                                                  Person.of(3, "park", 180));

        final List<Map<String, Object>> test = people.stream().map(Person::toMap).collect(Collectors.toList());
        return;
    }
}

 

분석

public static Map<String, Object> toMap(Person person) {
            try {
                Field[] fields = person.getClass().getDeclaredFields();
                Map<String, Object> results = new HashMap<>();
                for (Field field : fields) {
                    results.put(field.getName(), field.get(person));
                }
                return results;

            } catch (IllegalAccessException | IllegalArgumentException e) {
                return null;
            }

        }

toMap 메서드를 클래스 내에 정의했다. 이 메서드의 역할은 Person 객체의 field 이름 별로, 필드 이름과 값을 뽑아내

Map 객체에 주입하는 것이다. 그리고 그 Map 객체를 반환한다.

테스트


public class streamPractice {
	// 클래스 정의 코드 생략
    @Test
    public void Test() {

        final List<Person> people = Arrays.asList(Person.of(1, "choi", 170),
                                                  Person.of(2, "kim", 190),
                                                  Person.of(3, "park", 180));

        final List<Map<String, Object>> collectedPeople = people.stream().map(Person::toMap).collect(Collectors.toList());
        return;
    }
}

자 이제 테스트를 해보자. 먼저 Person 객체들을 담은 리스트를 선언한다. (of 메서드는 정적팩토리 메서드이다.)

stream 의 map메서드는 람다식이나 메서드 참조를 통해, 리스트 내 객체들에 대해 각각 로직을 처리할 수 있다.

그 다음 그 결과물은 collect() 를 통해 지정된 객체 배열 혹은 집합 혹은 맵에 담기게 된다.

 

위 함수는 toMap 을 통해 list 내 Person 객체별로 가공되어 test 객체에 담기게 된다.

 

custom 객체를 Map<K,V> 로 변환

 

이 방법을 이용하면, height 를 포함하지 않거나, 추가적인 로직을 통해 다른 값들도 주입할 수 있는 등 유연한 변화가 가능하다.

 

방법2

ObjectMapper 활용

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class streamPractice {

    public static class Person {
        private int id;
        private String name;
        private int height;

        private Person(int id, String name, int height) {
            this.id = id;
            this.name = name;
            this.height = height;
        }

        public int getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        public int getHeight() {
            return height;
        }

        public static Person of(int id, String name, int height) {
            return new Person(id,name,height);
        }

    }

    @Test
    public void Test() {

        final List<Person> people = Arrays.asList(Person.of(1, "choi", 170),
                                                  Person.of(2, "kim", 190),
                                                  Person.of(3, "park", 180));

   
        final ObjectMapper objectMapper = new ObjectMapper();
        final List<Object> collectedPeople = objectMapper.convertValue(people, new TypeReference<List<Object>>() {});

        return;
    }
}

훨씬 간단하다. ObjectMapper 는 Java Object 와 JSON 간 직렬화 및 역직렬화를 위한 라이브러리인데, 적절하게

TypeReference를 지정해줌으로 간단하게 List<Person> 을 List<Map<K,V>> 형태로 변환해줄 수 있다..

 

앞서 한 방법1이 의미있나 싶을 정도로 간단하다. 더 공부해봐야겠지만 차이점은 변환시 자유도가 방법1에 비해 떨어진다가 있을 것 같다.

(필터링이 얼마나 되나가 중요할듯)

 

애초에 역할자체가 JSON 객체로의 변환이 목적이기 때문에 그럴 것 같다고 추측한다.

 

결과는 방법1과 거의 같지만 차이점은, 방법2는 Map 의 구현체가 LinkedHashMap 이라는 차이가 있다.

 

LinkedHashMap 으로 구현되어있다.

 

각 방법은 장단점이 확실히 다른 듯 하다. 상황에 맞게 잘 선택할 수 있도록 java 8 stream 활용방법과 ObjectMapper 를 활용한 방법 모두 알아두면 좋을 것 같다.