삽질일기

[JAVA] Stream을 사용하여 Map의 key와 value들을 추출하기 (Collectors.groupingBy(), Collectors.mapping())

키크니개발자 2022. 7. 17. 23:33

💡 코드가 보이지 않으시다면 드래그 혹은 오른쪽 아래 🌜 아이콘을 눌러 테마 색을 변경해주세요.

 

 

안녕하세요!

키크니 개발자 입니다. 🦒

 

배송추적 관련 API를 연동하면서 

List<Object> 에서 Object 중 하나의 필드를 key(String)로 설정하고,

그 외의 필드 중 하나를 value(List<String>)로 삼고 싶었습니다.

 

상황

@Getter
@AllArgsConstructor
public class Shipping { 
	
    private String invoiceNo;
    private String status;
}
[
    {
        "invoiceNo" : "invoiceNo1",
        "status" : "배송준비중"
    },
    {
        "invoiceNo" : "invoiceNo2",
        "status" : "배송준비중"
    },
    {
        "invoiceNo" : "invoiceNo3",
        "status" : "배송준비중"
    },
    {
        "invoiceNo" : "invoiceNo1",
        "status" : "배송중"
    },
    {
        "invoiceNo" : "invoiceNo3",
        "status" : "배송중"
    },
    {
        "invoiceNo" : "invoiceNo3",
        "status" : "배송완료"
    },
]

위 처럼 구성 된 데이터에서 invoiceNo(운송장번호)를 기준으로 배송상태값("배송준비중", "배송중", "배송완료")을 뽑아

Map<String, List<String>> 의 형태로 만들고 싶었습니다.

ex : "invoiceNo1" : ["배송준비중", "배송중"], "invoiceNo2" : ["배송준비중"], "invoiceNo3" : ["배송준비중", "배송중", "배송완료"]

 

이를 for문을 2번 돌려야 하나 고민하고 있었지만, 

추후 많은 데이터를 다뤄야할 수 있었기 때문에 for문을 2번 돌리지 않는 것이 좋겠다는 조언을 들은 후 

어떻게 해야할지 찾아보았습니다.

 

일단 먼저 invoiceNo(운송장번호)를 Map의 Key로 묶어줘야했으므로 Collectors.GroupingBy()를 사용했습니다.

Collectors.groupingBy()

- 데이터를 그룹핑해서 Map으로 리턴합니다. 

- Thread에는 safe 하지 않습니다.

Lists.newArrayList()
	.stream()
	.collect(Collectors.groupingBy(o -> o));

Collectors.groupingByConcurrent()

- Thread에 safe 합니다.

Lists.newArrayList()
	.stream()
	.collect(Collectors.groupingByConcurrent(o -> o));

 

그 다음으로는 배송상태값(status)을 key에 맞게 value를 묶어줘야하는 작업을 해야했습니다.

해당 값을 추출하기 위해서 Collectors.mapping()을 사용했습니다.

Collectors.mapping()

리턴타입 method(매개 변수) 설명
Collector<T, ?, R> mapping(Function<T, U> mapper,
Collector<U, A, R> collector>
T를 U로 매핑한 후, U를 R에 수집합니다.
import java.util.*;
import java.util.stream.Collectors;

public class Main {

	public static void main(String[] args) {
		List<Shipping> shippingList = Arrays.asList(
				new Shipping("invoiceNo1", "배송준비중"),
				new Shipping("invoiceNo2", "배송준비중"),
				new Shipping("invoiceNo3", "배송준비중"),
				new Shipping("invoiceNo1", "배송중"),
				new Shipping("invoiceNo3", "배송중"),
				new Shipping("invoiceNo3", "배송완료")
		);

		Map<String, List<String>> shippingMap = shippingList.stream()
				.collect(Collectors.groupingBy(
						Shipping::invoiceNo,
						Collectors.mapping(Shipping::status, Collectors.toList()))
				);

		System.out.print("[invoiceNo1] ");
		shippingMap.get("invoiceNo1").forEach(status -> System.out.print(status + ", "));

		System.out.print("\n[invoiceNo2] ");
		shippingMap.get("invoiceNo2").forEach(status -> System.out.print(status + ", "));
		System.out.println();

		System.out.print("\n[invoiceNo3] ");
		shippingMap.get("invoiceNo3").forEach(status -> System.out.print(status + ", "));
	}

}

Shipping(T)를 status(U)로 매핑한 후, List(U)을 Map<String, List<String>> 중 List<String>(R)에 수집하는 것을 의미합니다.

위와 같은 코드를 작성하면 Shipping의 invoiceNo를 기준(key로 삼아)으로 각각의 status를 List으로 담아줄 수 있습니다.

 

결과 값

[invoiceNo1] 배송준비중, 배송중,
[invoiceNo2] 배송준비중,
[invoiceNo3] 배송준비중, 배송중, 배송완료

 

Collectors.groupingBy() method는 그룹핑 후,

매핑이나 집계를 할 수 있도록 두 번째 매개값으로 Collector를 가질 수 있습니다.

 

더 자세한 Collectors.groupingBy()의 method

더보기
리턴 타입 메소드(매개 변수) 설명
Collector<T, ?, R> mapping(Function<T, U> mapper, Collector<U, A, R> collector> T를 U로 매핑한 후, U를 R에 수집
Collector<T, ?, Double> averagingDouble(ToDoubleFunction<T> mapper) T를 Double로 매핑한 후, Double의 평균값을 산출
Collector<T, ?, Long> counting() T의 카운팅 수를 산출
Collector<CharSequence, ?, String> joining(CharSequence delimiter) CharSequence를 구분자로 연결한 String을 산출
Collector<T, ?, Optional<T>> maxBy(Comparator<T> comparator) Comparator를 이용해서 최대 T를 산출
Collector<T, ?, Optional<T>> minBy(Comparator<T> comparator) Comparator를 이용해서 최소 T를 산출
Collector<T, ?, Integer> summingInt(ToIntFunction)
summingLong(ToLongFunction)
summingDouble(ToDoubleFunction)
Int, Long, Double 타입의 합계 산출

 

⭐️  참고한 곳  


https://akageun.github.io/2019/08/06/java-stream-groupby.html

https://steady-coding.tistory.com/318

 

 

배워야 할 것이 더 많은 주니어 개발자입니다. 🐣
내용 전달보다는 정리를 목적으로 포스팅을 하고 있습니다.
잘못 된 내용이나 부족한 부분은 댓글로 주시면 감사드리겠습니다. 

 

반응형