목표
객체의 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 객체에 담기게 된다.
이 방법을 이용하면, 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 이라는 차이가 있다.
각 방법은 장단점이 확실히 다른 듯 하다. 상황에 맞게 잘 선택할 수 있도록 java 8 stream 활용방법과 ObjectMapper 를 활용한 방법 모두 알아두면 좋을 것 같다.
'프로그래밍 > JAVA' 카테고리의 다른 글
[Java] 문자열 붙이기에 반드시 StringBuilder 를 안해도 된다 (1) | 2021.06.05 |
---|---|
[Java] Java Servlet Cookie 생성 및 관리 (0) | 2021.05.13 |
[Java] Optional 에 대해 알아보자 - 2 (0) | 2021.04.21 |
[Java] Optional 에 대해 알아보자 - 1 (0) | 2021.03.29 |
[Java] 내가 만든 클래스 객체 배열 정렬하기 (comparable, comparator, comparing) (0) | 2021.02.14 |