combiner
Streams reduce
방식 에서 이행 하는 역할을 완전히 이해하는 데 어려움을 겪고 있습니다.
예를 들어 다음 코드는 컴파일되지 않습니다.
int length = asList("str1", "str2").stream()
.reduce(0, (accumulatedInt, str) -> accumulatedInt + str.length());
컴파일 오류 :
(인수 불일치; int를 java.lang.String으로 변환 할 수 없음)
그러나이 코드는 컴파일합니다 :
int length = asList("str1", "str2").stream()
.reduce(0, (accumulatedInt, str ) -> accumulatedInt + str.length(),
(accumulatedInt, accumulatedInt2) -> accumulatedInt + accumulatedInt2);
결합기 방법이 병렬 스트림에서 사용된다는 것을 이해합니다. 따라서 예제에서는 두 개의 중간 누적 정수를 더합니다.
그러나 첫 번째 예제가 결합기없이 컴파일되지 않는 이유 또는 결합기가 두 개의 정수를 더하기 때문에 문자열을 int로 변환하는 방법을 이해하지 못합니다.
누구든지 이것에 빛을 비출 수 있습니까?
답변
reduce
사용하려고 시도한 2 ~ 3 개의 인수 버전이 에 동일한 유형을 허용하지 않습니다 accumulator
.
두 인수 reduce
는 다음과 같이 정의됩니다 .
T reduce(T identity,
BinaryOperator<T> accumulator)
귀하의 경우 T는 문자열이므로 BinaryOperator<T>
두 개의 문자열 인수를 허용하고 문자열을 반환해야합니다. 그러나 int와 String을 전달하면 컴파일 오류가 발생합니다 argument mismatch; int cannot be converted to java.lang.String
. 실제로 String이 예상되기 때문에 (T) ID 값도 0이므로 0을 전달하는 것으로 생각합니다.
또한이 버전의 reduce는 Ts 스트림을 처리하고 T를 반환하므로 String 스트림을 int로 줄이는 데 사용할 수 없습니다.
세 가지 인수 reduce
는 다음과 같이 정의됩니다 .
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
귀하의 경우 U는 정수이고 T는 문자열 이므로이 방법은 문자열 스트림을 정수로 줄입니다.
들어 BiFunction<U,? super T,U>
누적 당신은 당신의 경우에 정수와 문자열을 두 개의 서로 다른 유형 (U와? 슈퍼 T)의 매개 변수를 전달할 수 있습니다. 또한 ID 값 U는 귀하의 경우 정수를 허용하므로 0을 전달하는 것이 좋습니다.
원하는 것을 달성하는 또 다른 방법 :
int length = asList("str1", "str2").stream().mapToInt (s -> s.length())
.reduce(0, (accumulatedInt, len) -> accumulatedInt + len);
여기서 스트림 유형은의 반환 유형과 일치 reduce
하므로의 두 매개 변수 버전을 사용할 수 있습니다 reduce
.
물론 전혀 사용할 필요가 없습니다 reduce
.
int length = asList("str1", "str2").stream().mapToInt (s -> s.length())
.sum();
답변
, 둘다의 답변 의 2 및 3 ARG ARG 버전 차이 기재된 reduce
전자가 감소 시킴에 Stream<T>
에 T
후자는 감소하는 반면 Stream<T>
에이 U
. 그러나 실제로로 축소 Stream<T>
할 때 추가 결합기 기능의 필요성을 설명하지는 않았습니다 U
.
Streams API의 디자인 원칙 중 하나는 API가 순차적 스트림과 병렬 스트림간에 다르지 않아야하거나 다른 방법으로 특정 스트림이 순차적으로 또는 병렬로 스트림이 올바르게 실행되는 것을 막지 않아야한다는 것입니다. 람다에 올바른 속성 (연관, 비 간섭 등)이있는 경우 순차적으로 또는 병렬로 실행되는 스트림은 동일한 결과를 제공해야합니다.
먼저 두 가지 버전의 축소를 고려해 보겠습니다.
T reduce(I, (T, T) -> T)
순차적 구현은 간단합니다. 항등 값 I
은 0 번째 스트림 요소와 함께 “누적”되어 결과를 제공합니다. 이 결과는 제 1 스트림 요소와 함께 누적되어 다른 결과를 제공하며, 결과적으로 제 2 스트림 요소와 함께 누적되는 식으로 진행된다. 마지막 요소가 누적 된 후 최종 결과가 반환됩니다.
병렬 구현은 스트림을 세그먼트로 분할하여 시작합니다. 각 세그먼트는 위에서 설명한 순서대로 자체 스레드에 의해 처리됩니다. 이제 N 개의 스레드가 있으면 N 개의 중간 결과가 나타납니다. 이것들은 하나의 결과로 줄여야합니다. 각 중간 결과는 유형 T이고 여러 개의 결과가 있으므로 동일한 누산기 함수를 사용하여 N 개의 중간 결과를 단일 결과로 줄일 수 있습니다.
이제 감소 가상의 두 인수 감소 동작을 살펴 보자 Stream<T>
에를 U
. 다른 언어에서는 이것을 “접음” 또는 “왼쪽 접힘”조작이라고하므로 여기에이를 호출합니다. Java에는 존재하지 않습니다.
U foldLeft(I, (U, T) -> U)
ID 값 I
은 U 유형입니다.
순차 버전은 중간 값이 T 유형 대신 U 유형이라는 점을 제외하고 foldLeft
는 순차 버전과 reduce
같습니다. 그러나 그렇지 않으면 동일합니다. (가설 foldRight
작업은 왼쪽에서 오른쪽 대신 오른쪽에서 왼쪽으로 수행된다는 점을 제외하면 비슷합니다.)
이제의 병렬 버전을 고려하십시오 foldLeft
. 스트림을 세그먼트로 분할하여 시작하겠습니다. 그런 다음 각 N 스레드가 세그먼트의 T 값을 U 유형의 N 중간 값으로 줄 이도록 할 수 있습니다. 이제 무엇? U 유형의 N 값에서 U 유형의 단일 결과까지 어떻게 얻습니까?
누락 된 것은 U 유형의 여러 중간 결과를 U 유형의 단일 결과로 결합 하는 또 다른 함수입니다. 두 개의 U 값을 하나로 결합하는 함수가 있으면 값을 하나로 줄이면 충분합니다. 위의 원래 축소. 따라서 다른 유형의 결과를 제공하는 축소 연산에는 두 가지 기능이 필요합니다.
U reduce(I, (U, T) -> U, (U, U) -> U)
또는 Java 구문을 사용하십시오.
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
요약하면, 다른 결과 유형으로 병렬 축소를 수행하려면 T 요소를 중간 U 값으로 누적 하는 함수 와 중간 U 값을 단일 U 결과로 결합 하는 함수가 필요합니다 . 전환 유형이 아닌 경우 누산기 기능이 결합기 기능과 동일하다는 것이 밝혀졌습니다. 그렇기 때문에 같은 유형으로 줄이면 누산기 기능 만 있고 다른 유형으로 줄이면 별도의 누산기와 결합기 기능이 필요합니다.
마지막으로, 자바는 제공하지 않습니다 foldLeft
및 foldRight
그들이 본질적으로 순차적 인 작업의 특정 순서를 의미하기 때문에 작업. 이는 순차적 및 병렬 작업을 동일하게 지원하는 API를 제공하는 위에서 언급 한 설계 원칙과 충돌합니다.
답변
개념을 명확하게하기 위해 낙서와 화살표를 좋아하기 때문에 시작합시다!
문자열에서 문자열로 (순차 스트림)
4 개의 문자열이 있다고 가정하십시오. 목표는 이러한 문자열을 하나로 연결하는 것입니다. 기본적으로 유형으로 시작하고 동일한 유형으로 완료합니다.
당신은 이것을 달성 할 수 있습니다
String res = Arrays.asList("one", "two","three","four")
.stream()
.reduce("",
(accumulatedStr, str) -> accumulatedStr + str); //accumulator
그리고 이것은 무슨 일이 일어나고 있는지 시각화하는 데 도움이됩니다.
누산기 기능은 (빨간색) 스트림의 요소를 단계적으로 줄어든 최종 녹색 값으로 변환합니다. 누산기 함수는 단순히 String
객체를 다른 객체로 변환합니다 String
.
String에서 int로 (병렬 스트림)
동일한 4 개의 문자열이 있다고 가정합니다. 새로운 목표는 길이를 합산하고 스트림을 병렬화하려는 것입니다.
필요한 것은 다음과 같습니다.
int length = Arrays.asList("one", "two","three","four")
.parallelStream()
.reduce(0,
(accumulatedInt, str) -> accumulatedInt + str.length(), //accumulator
(accumulatedInt, accumulatedInt2) -> accumulatedInt + accumulatedInt2); //combiner
그리고 이것은 일어나고있는 일의 계획입니다
여기서 누산기 기능 (a BiFunction
)을 사용하면 String
데이터를 데이터로 변환 할 수 있습니다 int
. 스트림이 평행하기 때문에 두 부분 (빨간색)으로 나뉘며, 각 부분은 서로 독립적으로 정교하게 만들어졌으며 부분적인 (주황색) 결과와 거의 같습니다. 부분 int
결과를 최종 (녹색) 결과로 병합하기위한 규칙을 제공하려면 결합기를 정의해야합니다 int
.
String에서 int (순차 스트림)
스트림을 병렬화하지 않으려면 어떻게해야합니까? 어쨌든 컴 바이 너를 제공해야하지만 부분 결과가 생성되지 않으면 호출되지 않습니다.
답변
병렬로 실행할 수 없으므로 결합기가 없는 두 가지 유형을 사용하는 축소 버전 은 없습니다 (이것이 왜 필요한지 잘 모르겠습니다). 누산기 가 연결되어야 한다는 사실 때문에 다음과 같은 이유로이 인터페이스를 거의 쓸모 없게 만듭니다.
list.stream().reduce(identity,
accumulator,
combiner);
다음과 같은 결과를 생성합니다.
list.stream().map(i -> accumulator(identity, i))
.reduce(identity,
combiner);