Java

Java 8 Collectors 베스트 예제

기록만이살길 2020. 6. 24. 01:48
반응형

Java 8 Collectors 베스트 예제

1. 개요

Stream 처리의 마지막 단계에서 사용되는 Java 8 Collectors 살펴 보겠습니다 .

2. Stream.collect () 메소드

Stream.collect () 는 Java 8의 Stream API 터미널 메소드 중 하나입니다 . 이를 통해 Stream 인스턴스에 보유 된 데이터 요소에 대해 변경 가능한 접기 작업 (요소를 일부 데이터 구조로 재 포장 및 추가 논리 적용, 연결 등)을 수행 할 수 있습니다.

이 작업에 대한 전략은 수집기 인터페이스 구현을 통해 제공됩니다 .

3. Collectors

미리 정의 된 모든 구현은 Collectors 클래스 에서 찾을 수 있습니다 . 가독성을 높이기 위해 다음 정적 가져 오기를 사용하는 것이 일반적입니다.

import static java.util.stream.Collectors.*;

또는 선택한 단일 가져 오기 수집기 :

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;

다음 예제에서는 다음 목록을 재사용합니다.

List<String> givenList = Arrays.asList("a", "bb", "ccc", "dd");

3.1. Collectors.toList ()

ToList 수집기는 모든 Stream 요소를 List 인스턴스 로 수집하는 데 사용할 수 있습니다 . 기억해야 할 중요한 것은 이 메소드를 사용하여 특정 List 구현을 가정 할 수 없다는 사실입니다 . 이것을 더 잘 제어하려면 대신 toCollection을 사용하십시오.

하자가 생성 스트림 요소의 순서를 나타내는 인스턴스를하고로를 수집 목록의 예 :

List<String> result = givenList.stream()
  .collect(toList());

3.2. Collectors.toSet ()

ToSet 수집기는 모든 Stream 요소를 Set 인스턴스 로 수집하는 데 사용할 수 있습니다 . 기억해야 할 중요한 것은 이 메소드를 사용하여 특정 Set 구현을 가정 할 수 없다는 사실입니다 . 이것을 더 잘 제어하려면 toCollection을 대신 사용할 수 있습니다 .

의는 만들어 보자 스트림 요소의 순서를 나타내는 인스턴스를하고로 수집 설정 예 :

Set<String> result = givenList.stream()
  .collect(toSet());

세트는 중복 요소를 포함하지 않습니다. 컬렉션에 서로 같은 요소가 포함 된 경우 결과 집합에 한 번만 나타납니다 .

List<String> listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");
Set<String> result = listWithDuplicates.stream().collect(toSet());
assertThat(result).hasSize(4);

3.3. Collectors.toCollection ()

이미 알고 있듯이 toSet 및 toList 콜렉터를 사용할 때는 해당 구현을 가정 할 수 없습니다. 사용자 정의 구현을 사용하려면 제공된 콜렉션을 선택 하여 toCollection 콜렉터 를 사용해야합니다 .

의는 만들어 보자 스트림 요소의 순서를 나타내는 인스턴스를하고로를 수집 LinkedList의의 예 :

List<String> result = givenList.stream()
  .collect(toCollection(LinkedList::new))

변경 불가능한 콜렉션에서는 작동하지 않습니다. 이 경우 사용자 정의 수집기 구현을 작성하거나 collectAndThen을 사용해야 합니다 .

3.4. Collectors.toMap()

ToMap 수집기는 Stream 요소를 Map 인스턴스 로 수집하는 데 사용할 수 있습니다 . 이렇게하려면 두 가지 기능을 제공해야합니다.

  • keyMapper
  • valueMapper

keyMapperStream 요소 에서 Map 키 를 추출하는 데 사용 되며 valueMapper 는 지정된 키와 관련된 값을 추출하는 데 사용됩니다.

문자열을 키로 저장하고 길이를 값으로 저장 하는 Map에 해당 요소를 수집합시다 .

Map<String, Integer> result = givenList.stream()
  .collect(toMap(Function.identity(), String::length))

Function.identity () 는 동일한 값을 수락하고 반환하는 함수를 정의하기위한 바로 가기입니다.

컬렉션에 중복 요소가 포함되어 있으면 어떻게됩니까? toSet 과 달리 toMap 은 중복을 자동으로 필터링하지 않습니다. 이해할 수 있습니다.이 키에 어떤 값을 선택해야하는지 어떻게 알 수 있습니까?

List<String> listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");
assertThatThrownBy(() -> {
    listWithDuplicates.stream().collect(toMap(Function.identity(), String::length));
}).isInstanceOf(IllegalStateException.class);

참고 toMap이 값도 동일 여부도 평가하지 않습니다. 중복 키가 보이면 즉시 IllegalStateException을 발생 시킵니다.

키 충돌이있는 경우 다른 서명과 함께 toMap 을 사용해야합니다 .

Map<String, Integer> result = givenList.stream()
  .collect(toMap(Function.identity(), String::length, (item, identicalItem) -> item));

여기서 세 번째 인수는 BinaryOperator 이며 충돌 처리 방법을 지정할 수 있습니다. 이 경우 동일한 문자열의 길이가 항상 동일하다는 것을 알기 때문에이 두 충돌 값 중 하나만 선택합니다.

3.5. Collectors.collectingAndThen()

CollectingAndThen수집 이 끝난 직후 결과에 대해 다른 작업을 수행 할 수있는 특수 수집기입니다.

Stream 요소를 List 인스턴스 로 수집 한 다음 결과를 ImmutableList 인스턴스 로 변환합니다 .

List<String> result = givenList.stream()
  .collect(collectingAndThen(toList(), ImmutableList::copyOf))

3.6. Collectors.joining()

Stream 요소 를 결합하는 데 결합 수집기를 사용할 수 있습니다 .

다음을 수행하여 함께 참여할 수 있습니다.

String result = givenList.stream()
  .collect(joining());

결과는 다음과 같습니다.

"abbcccdd"

사용자 정의 구분 기호, 접두사, 접미사를 지정할 수도 있습니다.

String result = givenList.stream()
  .collect(joining(" "));

결과는 다음과 같습니다.

"a bb ccc dd"

또는 당신은 쓸 수 있습니다 :

String result = givenList.stream()
  .collect(joining(" ", "PRE-", "-POST"));

결과는 다음과 같습니다.

"PRE-a bb ccc dd-POST"

3.7. Collectors.counting()

카운팅 은 모든 스트림 요소를 간단히 카운팅 할 수있는 간단한 수집기입니다 .

이제 다음과 같이 쓸 수 있습니다 :

Long result = givenList.stream()
  .collect(counting());

3.8. Collectors.summarizingDouble/Long/Int()

SummarizingDouble / Long / Int 는 추출 된 요소 스트림 에서 숫자 데이터에 대한 통계 정보를 포함하는 특수 클래스를 리턴하는 콜렉터입니다 .

다음을 수행하여 문자열 길이에 대한 정보를 얻을 수 있습니다.

DoubleSummaryStatistics result = givenList.stream()
  .collect(summarizingDouble(String::length));

이 경우 다음이 적용됩니다.

assertThat(result.getAverage()).isEqualTo(2);
assertThat(result.getCount()).isEqualTo(4);
assertThat(result.getMax()).isEqualTo(3);
assertThat(result.getMin()).isEqualTo(1);
assertThat(result.getSum()).isEqualTo(8);

3.9. Collectors.averagingDouble/Long/Int()

AveragingDouble / Long / Int 는 추출 된 요소의 평균을 반환하는 수집기입니다.

다음을 수행하여 평균 문자열 길이를 얻을 수 있습니다.

Double result = givenList.stream()
  .collect(averagingDouble(String::length));

3.10. Collectors.summingDouble/Long/Int()

SummingDouble / Long / Int 는 추출 된 요소의 합계를 반환하는 수집기입니다.

다음을 수행하여 모든 문자열 길이의 합계를 얻을 수 있습니다.

Double result = givenList.stream()
  .collect(summingDouble(String::length));

3.11. Collectors.maxBy()/minBy()

MaxBy / MinBy 수집기 는 제공된 Comparator 인스턴스 에 따라 스트림 의 가장 큰 / 가장 작은 요소를 반환합니다 .

다음을 수행하여 가장 큰 요소를 선택할 수 있습니다.

Optional<String> result = givenList.stream()
  .collect(maxBy(Comparator.naturalOrder()));

반환 된 값은 Optional 인스턴스에 래핑됩니다 . 이로 인해 사용자는 빈 수집 코너 케이스를 다시 생각해야합니다.

3.12. Collectors.groupingBy()

GroupingBy 콜렉터는 일부 특성별로 오브젝트를 그룹화하고 결과를 Map 인스턴스 에 저장하는 데 사용됩니다 .

문자열 길이별로 그룹화하고 그룹화 결과를 Set 인스턴스 에 저장할 수 있습니다 .

Map<Integer, Set<String>> result = givenList.stream()
  .collect(groupingBy(String::length, toSet()));

결과는 다음과 같습니다.

assertThat(result)
  .containsEntry(1, newHashSet("a"))
  .containsEntry(2, newHashSet("bb", "dd"))
  .containsEntry(3, newHashSet("ccc"));

주의의 두 번째 인수 것을 groupingBy의 방법은이다 수집기 당신은 어떤 자유롭게 사용할 수 있습니다 수집기를 원하는.

3.13. Collectors.partitioningBy ()

PartitioningByPredicate 인스턴스 를 허용하고 스트림 요소를 부울 값을 키로, 컬렉션을 값으로 저장 하는 Map 인스턴스 로 수집 하는 groupingBy 의 특수한 경우입니다 . 은 "진정한"키 아래에, 당신은 주어진 일치하는 요소의 모음 찾을 수 있습니다 술어를 하고 "거짓"키에, 당신은 주어진 일치하지 않는 요소의 컬렉션을 찾을 수 있습니다 술어를 .

당신은 쓸 수 있습니다:

Map<Boolean, List<String>> result = givenList.stream()
  .collect(partitioningBy(s -> s.length() > 2))

다음을 포함하는 맵이 생성됩니다.

{false=["a", "bb", "dd"], true=["ccc"]}

3.14. Collectors.teeing ()

지금까지 배운 콜렉터를 사용하여 지정된 스트림 에서 최대 및 최소 수를 찾으십시오 .

List<Integer> numbers = Arrays.asList(42, 4, 2, 24);
Optional<Integer> min = numbers.stream().collect(minBy(Integer::compareTo));
Optional<Integer> max = numbers.stream().collect(maxBy(Integer::compareTo));
// do something useful with min and max

여기서 우리는 두 개의 다른 수집기를 사용하고 그 두 개의 결과를 결합하여 의미있는 것을 만듭니다. Java 12 이전에는 이러한 사용 사례를 다루기 위해 주어진 스트림에서 두 번 작업하고 중간 결과를 임시 변수에 저장 한 다음 그 결과를 결합해야했습니다.

다행히도 Java 12는 내장 된 수집기를 제공하여이를 대신하여 이러한 단계를 처리합니다. 두 수집기와 결합기 기능을 제공하기 만하면됩니다.

이 새로운 컬렉터 때문에 두 개의 서로 다른 방향을 향해 지정된 스트림을, 그것은라고 티잉 :

numbers.stream().collect(teeing(
  minBy(Integer::compareTo), // The first collector
  maxBy(Integer::compareTo), // The second collector
  (min, max) -> // Receives the result from those collectors and combines them
));

이 예제는 core-java-12 프로젝트의 GitHub에서 사용할 수 있습니다 .

4. 커스텀 컬렉터

Collector 구현을 작성하려면 Collector 인터페이스를 구현하고 세 가지 일반 매개 변수를 지정해야합니다.

public interface Collector<T, A, R> {...}
  1. T

    – 수집에 사용 가능한 객체의 유형

  2. A

    – 변경 가능한 누산기 객체의 유형

  3. R

    – 최종 결과의 유형.

ImmutableSet 인스턴스에 요소를 수집하기위한 Collector 예제를 작성해 봅시다 . 올바른 유형을 지정하여 시작합니다.

private class ImmutableSetCollector<T>
  implements Collector<T, ImmutableSet.Builder<T>, ImmutableSet<T>> {...}

내부 컬렉션 작업 처리를 위해 변경 가능한 컬렉션이 필요하기 때문에 ImmutableSet 을 사용할 수 없습니다 . 우리는 일시적으로 객체를 축적 할 수있는 다른 변경 가능한 컬렉션이나 클래스를 사용해야합니다.이 경우에는 ImmutableSet.Builder 를 사용하여 5 가지 메소드를 구현해야합니다.

  • 공급자 <ImmutableSet.Builder > 공급자 ()
  • BiConsumer <ImmutableSet.Builder , T> 누산기 ()
  • BinaryOperator <ImmutableSet.Builder > 결합기 ()
  • 함수 <ImmutableSet.Builder , ImmutableSet > 마무리 장치 ()
  • *<특성> 특성 설정 ()*

supplier () 메소드는빈 누산기 인스턴스를 생성하는 Supplier 인스턴스를반환 하므로이 경우 간단히 작성할 수 있습니다.

@Override
public Supplier<ImmutableSet.Builder<T>> supplier() {
    return ImmutableSet::builder;
}

accumulator () 메소드는 기존 accumulator 객체에새 요소를 추가하는 데 사용되는 함수를 리턴하므로 Builderadd 메소드를사용하십시오.

@Override
public BiConsumer<ImmutableSet.Builder<T>, T> accumulator() {
    return ImmutableSet.Builder::add;
}

combiner () 메소드는 두 개의 누산기를 병합하는 데 사용되는 함수를 리턴합니다.

@Override
public BinaryOperator<ImmutableSet.Builder<T>> combiner() {
    return (left, right) -> left.addAll(right.build());
}

finisher () 메소드는 누산기를 최종 결과 유형으로 변환하는 데 사용되는 함수를 리턴 하므로이 경우 빌더빌드 메소드만 사용합니다.

@Override
public Function<ImmutableSet.Builder<T>, ImmutableSet<T>> finisher() {
    return ImmutableSet.Builder::build;
}

attributes () 메서드는 내부 최적화에 사용될 몇 가지 추가 정보를 Stream에 제공하는 데 사용됩니다. 요소가에서 주문이 경우, 우리는 관심을 지불하지 않는 설정 우리가 사용되도록 Characteristics.UNORDERED을 . 이 주제에 대한 자세한 정보를 얻으려면 특성 'JavaDoc을확인하십시오.

@Override public Set<Characteristics> characteristics() {
    return Sets.immutableEnumSet(Characteristics.UNORDERED);
}

사용법과 함께 완전한 구현은 다음과 같습니다.

public class ImmutableSetCollector<T>
  implements Collector<T, ImmutableSet.Builder<T>, ImmutableSet<T>> {

@Override
public Supplier<ImmutableSet.Builder<T>> supplier() {
    return ImmutableSet::builder;
}

@Override
public BiConsumer<ImmutableSet.Builder<T>, T> accumulator() {
    return ImmutableSet.Builder::add;
}

@Override
public BinaryOperator<ImmutableSet.Builder<T>> combiner() {
    return (left, right) -> left.addAll(right.build());
}

@Override
public Function<ImmutableSet.Builder<T>, ImmutableSet<T>> finisher() {
    return ImmutableSet.Builder::build;
}

@Override
public Set<Characteristics> characteristics() {
    return Sets.immutableEnumSet(Characteristics.UNORDERED);
}

public static <T> ImmutableSetCollector<T> toImmutableSet() {
    return new ImmutableSetCollector<>();
}

그리고 여기 실제로 행동하십시오 :

List<String> givenList = Arrays.asList("a", "bb", "ccc", "dddd");

ImmutableSet<String> result = givenList.stream()
  .collect(toImmutableSet());

5. 결론

이 기사에서는 자세한 Java 8 수집기 를 살펴보고 구현 방법을 보여주었습니다. Java에서 병렬 처리 기능을 향상시키는 프로젝트 중 하나를 확인하십시오 .

참고

https://www.baeldung.com/java-8-collectors

반응형

'Java' 카테고리의 다른 글

Keycloak의 로그인 페이지 사용자 정의  (0) 2021.09.20
Java 8 Stream 불변 Collection  (0) 2020.06.27
Java 8 groupingBy Collector 예제  (1) 2020.06.25
Java 8 Functional Interfaces  (0) 2020.06.22
Java 8 Stream findFirst findAny 차이  (0) 2020.06.21