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
keyMapper 는 Stream 요소 에서 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 ()
PartitioningBy 는 Predicate 인스턴스 를 허용하고 스트림 요소를 부울 값을 키로, 컬렉션을 값으로 저장 하는 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> {...}
T
– 수집에 사용 가능한 객체의 유형
A
– 변경 가능한 누산기 객체의 유형
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 객체에새 요소를 추가하는 데 사용되는 함수를 리턴하므로 Builder 의 add 메소드를사용하십시오.
@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에서 병렬 처리 기능을 향상시키는 프로젝트 중 하나를 확인하십시오 .
참고
'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 |