티스토리 뷰

Server

[Java] Stream GroupBy 사용하기

니용 2021. 12. 30. 18:00
반응형

이번 글에서는 자바에서 사용하는 그룹핑 방법을 간단히 소개해드리고자 글을 작성하게 되었습니다. 회사에서 사용하는 기술 중 하나는 JPA인데, JPA에서는 기본적으로 Group By 옵션을 사용할 수 없습니다. 적어도 CustomRepository 를 만들어 Native Query 로 작성을 하거나, QueryDSL 을 사용해서 쿼리를 변경해야 합니다. 이 글은 MyBatis의 방법으로 GROUP BY 를 사용하는 대신 자바에서 사용할 수 있는 방법을 제시하고자 합니다.  

 

Collertors 클래스 사용하기

1. groupingBy()

데이터를 그룹핑해서 Map으로 리턴합니다. Thread safe 하지 않습니다.

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

 

2. groupingByConcurrent()

데이터를 그룹핑해서 Map으로 리턴하는 것은 groupingBy와 같은 방법이지만 Thread safe 합니다.

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

 

Example

model

@Getter
@Setter
@Builder
private static class Temp {
	private String group;
	private String uuid;
}

Test

@Test
public void listSample() {
	List<Temp> list = Arrays.asList(
		Temp.builder().group("A").uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group("A").uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group("B").uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group("C").uuid(UUID.randomUUID().toString()).build()
	);

	list.stream()
		.collect(Collectors.groupingBy(Temp::getGroup))
		.forEach((key, value) -> {
			log.info("key :{}, value : {}", key, value.size());
		});
}

결과

key :A, value : 2
key :B, value : 1
key :C, value : 1

 

주의할 점

Group By 의 Key 값이 Nullable 인 경우 NullPointerException 을 주의하여야 합니다.

Caused by: java.lang.NullPointerException: element cannot be mapped to a null key
    at java.util.Objects.requireNonNull(Objects.java:228)
    at java.util.stream.Collectors.lambda$groupingBy$45(Collectors.java:907)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)

대안코드

@Test
public void listSample() {
	List<Temp> list = Arrays.asList(
		Temp.builder().group("A").uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group("A").uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group("B").uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group("C").uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group(null).uuid(UUID.randomUUID().toString()).build(),
		Temp.builder().group(null).uuid(UUID.randomUUID().toString()).build()
	);

	list.stream()
		.collect(
			Collectors.toMap(
				Temp::getGroup,
				x -> {
					List<Temp> subList = new ArrayList<>();
					subList.add(x);
					return subList;
				},
				(left, right) -> {
					left.addAll(right);
					return left;
				},
				HashMap::new
			)
		)
		.forEach((key, value) -> {
			log.info("key :{}, value : {}", key, value.size());
		});
}

결과

key :null, value : 2
key :A, value : 2
key :B, value : 1
key :C, value : 1

참고링크

반응형
댓글
공지사항