Назначение третьего аргумента функции "уменьшить" в функциональном программировании Java 8
В каких обстоятельствах является третьим аргументом для "сокращения", вызванного в потоках Java 8?
Приведенный ниже код пытается пересечь список строк и складывать значения кодовой точки первого символа каждого из них. Значение, возвращаемое финальной лямбдой, никогда не используется, и если вы вставляете println, это никогда не будет вызвано. Документация описывает его как "объединитель", но я не могу найти более подробную информацию...
int result =
data.stream().reduce(0, (total,s) -> total + s.codePointAt(0), (a,b) -> 1000000);
Ответы
Ответ 1
Вы говорите эту функцию?
reduce <U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
Выполняет сокращение элементов этого потока, используя предоставленную идентификацию, накопление и объединение функций. Это эквивалентно:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
но не ограничивается последовательностью выполнения. Значение идентификатора должно быть идентификатором функции объединителя. Эта означает, что для всех u сумматор (тождество, u) равен u. Кроме того, функция объединителя должна быть совместима с аккумуляторная функция; для всех u и t должно выполняться следующее:
combiner.apply(u, accumulator.apply(identity, t)) ==
accumulator.apply(u, t)
Это операция терминала.
API Примечание. Многие сокращения, использующие эту форму, могут быть представлены более просто путем явной комбинации операций отображения и сокращения. функция аккумулятора действует как плавкий преобразователь и аккумулятор, который может иногда более эффективны, чем отдельное отображение и сокращение, поскольку, зная ранее уменьшенное значение, вы можете избежать некоторых вычисление. Тип Параметры: U - Тип результата Параметры: identity - идентификационное значение для накопителя функций объединителя - ассоциативная, невмешающая, функция без гражданства для включения дополнительный элемент в объединитель результатов - ассоциативный, неинтерферирующая функция без сохранения для объединения двух значений, которые должен быть совместим с функцией аккумулятора. Возвращает: результат сокращения. См. также: уменьшить (BinaryOperator), уменьшить (Object, BinaryOperator)
Я предполагаю, что его цель - разрешить параллельное вычисление, и поэтому я предполагаю, что он используется только в том случае, если редукция выполняется параллельно. Если он выполняется последовательно, нет необходимости использовать combiner
. Я не знаю этого наверняка - я просто догадываюсь на основе комментария к доктору: "[...] не ограничено выполнение последовательно" и многие другие упоминания о "параллельном выполнении" в комментариях.
Ответ 2
Я думаю, Операции сокращения от java.util.stream
сводка пакетов может ответить на вопрос. Позвольте мне привести наиболее важную часть здесь:
В более общем виде операция сокращения для элементов типа <T>
, дающая результат типа <U>
, требует трех параметров:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
Здесь элемент идентификации является как начальным начальным значением для сокращения, так и результатом по умолчанию, если нет входных элементов. Функция аккумулятора принимает частичный результат и следующий элемент и производит новый частичный результат. Функция объединителя объединяет два частичных результата для получения нового частичного результата. (Объединитель необходим в параллельных сокращениях, где вход разделен, частичное накопление, рассчитанное для каждого раздела, а затем частичные результаты объединяются для получения окончательного результата.)
Более формально значение идентификатора должно быть тождеством для функции объединителя. Это означает, что для всех u
combiner.apply(identity, u)
равно u
. Кроме того, функция объединителя должна быть ассоциативной и должна быть совместима с функцией аккумулятора: для всех u
и t
, combiner.apply(u, accumulator.apply(identity, t))
должен быть equals()
до accumulator.apply(u, t)
.
Форма с тремя аргументами является обобщением формы с двумя аргументами, включающей шаг отображения на этап накопления. Мы могли бы повторить простой пример суммы весов, используя более общую форму следующим образом:
int sumOfWeights = widgets.stream()
.reduce(0,
(sum, b) -> sum + b.getWeight())
Integer::sum);
хотя явная форма сокращения карты более читаема и поэтому обычно должна быть предпочтительной. Обобщенная форма предоставляется для случаев, когда значительная работа может быть оптимизирована путем объединения отображения и сокращения в одну функцию.
Другими словами, насколько я понимаю, форма трех аргументов полезна в двух случаях:
- При параллельном выполнении.
- Когда значительная оптимизация производительности может быть достигнута путем объединения шагов сопоставления и накопления. В противном случае может использоваться более простая и читаемая явная форма сокращения карты.
Явная форма упоминается ранее в одном документе:
int sumOfWeights = widgets.parallelStream()
.filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();
Ответ 3
Простой тестовый код для подтверждения использования объединителя:
String[] strArray = {"abc", "mno", "xyz"};
List<String> strList = Arrays.asList(strArray);
System.out.println("stream test");
int streamResult = strList.stream().reduce(
0,
(total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); },
(a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "]"); return 1000000;}
);
System.out.println("streamResult: " + streamResult);
System.out.println("parallelStream test");
int parallelStreamResult = strList.parallelStream().reduce(
0,
(total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); },
(a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "]"); return 1000000;}
);
System.out.println("parallelStreamResult: " + parallelStreamResult);
System.out.println("parallelStream test2");
int parallelStreamResult2 = strList.parallelStream().reduce(
0,
(total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); },
(a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "] a+b[" + (a+b) + "]"); return a+b;}
);
System.out.println("parallelStreamResult2: " + parallelStreamResult2);
Вывод:
stream test
accumulator: total[0] s[abc] s.codePointAt(0)[97]
accumulator: total[97] s[mno] s.codePointAt(0)[109]
accumulator: total[206] s[xyz] s.codePointAt(0)[120]
streamResult: 326
parallelStream test
accumulator: total[0] s[mno] s.codePointAt(0)[109]
accumulator: total[0] s[abc] s.codePointAt(0)[97]
accumulator: total[0] s[xyz] s.codePointAt(0)[120]
combiner: a[109] b[120]
combiner: a[97] b[1000000]
parallelStreamResult: 1000000
parallelStream test2
accumulator: total[0] s[mno] s.codePointAt(0)[109]
accumulator: total[0] s[xyz] s.codePointAt(0)[120]
accumulator: total[0] s[abc] s.codePointAt(0)[97]
combiner: a[109] b[120] a+b[229]
combiner: a[97] b[229] a+b[326]
parallelStreamResult2: 326