Вместо того, что вы сейчас используете, чтобы скрыть кнопку, было бы гораздо проще установить visibility: collapse;
в атрибуте style. Тем не менее, я бы порекомендовал использовать немного простого Javascript для отправки формы. Насколько я понимаю, поддержка таких вещей сегодня повсеместна.
Две и три версии аргументов reduce
, которые вы пытались использовать, не принимают один и тот же тип для accumulator
.
Два аргумента reduce
- , определенные как :
T reduce(T identity,
BinaryOperator<T> accumulator)
В вашем случае T является строкой, поэтому BinaryOperator<T>
должен принимать два аргумента String и возвращать String. Но вы передаете ему int и String, что приводит к ошибке компиляции - argument mismatch; int cannot be converted to java.lang.String
. На самом деле, я думаю, что передача 0, поскольку значение идентичности здесь также неверно, поскольку ожидается String (T).
Также обратите внимание, что эта версия сокращает процесс потока Ts и возвращает T, поэтому вы не может использовать его, чтобы уменьшить поток String до int.
Три аргумента reduce
- , определенные как :
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
In ваш случай U является Integer и T является String, поэтому этот метод уменьшит поток String до Integer.
Для аккумулятора BiFunction<U,? super T,U>
вы можете передавать параметры двух разных типов (U и? super T ), которые в вашем случае являются целыми и строковыми. Кроме того, значение 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();
Ответ Эрана описал различия между версиями reduce
с двумя аргументами и тремя аргументами в том, что первая уменьшает Stream<T>
до T
, тогда как последняя уменьшает Stream<T>
до U
. Тем не менее, на самом деле он не объяснил необходимость дополнительной функции объединителя при уменьшении Stream<T>
до U
.
. Один из принципов проектирования API Streams заключается в том, что API не должен отличаться последовательным и параллельным потокам или другим способом, конкретный API не должен препятствовать правильной работе потока последовательно или параллельно. Если ваши лямбды имеют правильные свойства (ассоциативные, неинтерферирующие и т. Д.), Поток, выполняемый последовательно или параллельно, должен давать одинаковые результаты.
Давайте сначала рассмотрим версию сокращения с двумя аргументами:
T reduce(I, (T, T) -> T)
Последовательная реализация проста. Значение идентичности I
«накапливается» с элементом нулевого потока, чтобы дать результат. Этот результат накапливается с первым элементом потока, чтобы дать другой результат, который, в свою очередь, накапливается со вторым элементом потока и т. Д. После того, как последний элемент накоплен, возвращается окончательный результат.
Параллельная реализация начинается с разделения потока на сегменты. Каждый сегмент обрабатывается собственным потоком последовательным образом, описанным выше. Теперь, если у нас есть N потоков, мы имеем N промежуточных результатов. Их необходимо сократить до одного результата. Поскольку каждый промежуточный результат имеет тип T, и у нас есть несколько, мы можем использовать одну и ту же функцию аккумулятора, чтобы уменьшить эти промежуточные результаты до одного результата.
Теперь рассмотрим гипотетическую операцию сокращения двух аргументов что уменьшает Stream<T>
до U
. В других языках это называется операцией «fold» или «fold-left», поэтому я назову ее здесь. Обратите внимание, что это не существует в Java.
U foldLeft(I, (U, T) -> U)
(Обратите внимание, что значение идентификатора I
имеет тип U.)
Последовательная версия foldLeft
как последовательная версия reduce
, за исключением того, что промежуточные значения имеют тип U вместо типа T. Но в остальном это одно и то же. (Гипотетическая операция foldRight
будет аналогичной, за исключением того, что операции будут выполняться справа налево, а не слева направо.)
Теперь рассмотрим параллельную версию foldLeft
. Начнем с разделения потока на сегменты. Затем мы можем иметь, что каждый из N потоков уменьшает значения T в своем сегменте на N промежуточных значений типа U. Теперь что? Как мы получаем от N значений типа U до одного результата типа 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)
Итак, чтобы сделать параллельное сокращение к другому типу результата, нам нужны две функции: одна, которая накапливает элементы Т до промежуточных значений U, и вторую, которая объединяет промежуточные значения U в единый результат U. Если мы не переключаем типы, получается, что функция аккумулятора такая же, как функция объединителя. Вот почему сокращение до одного и того же типа имеет только функцию аккумулятора, а уменьшение к другому типу требует отдельных функций аккумулятора и объединителя.
Наконец, Java не предоставляет операции foldLeft
и foldRight
, потому что они подразумевают конкретный порядок операций, который по своей сути является последовательным. Это противоречит описанному выше принципу проектирования предоставления API, которые поддерживают последовательную и параллельную работу в равной степени.
foldLeft
, потому что вычисление зависит от предыдущего результата и не может быть распараллелировано?
– amoebe
9 May 2015 в 18:51
forEachOrdered
. Однако промежуточное состояние должно храниться в захваченной переменной.
– Stuart Marks
10 May 2015 в 03:25
foldLeft
.
– amoebe
10 May 2015 в 13:12
Так как мне нравятся каракули и стрелки, чтобы прояснить понятия ... давайте начнем!
Предположим, что у вас есть 4 строки: ваша цель - объединить такие строки в одну. В основном вы начинаете с типа и заканчиваете с тем же типом.
Вы можете добиться этого с помощью
String res = Arrays.asList("one", "two","three","four")
.stream()
.reduce("",
(accumulatedStr, str) -> accumulatedStr + str); //accumulator
, и это поможет вам визуализировать происходящее:
Функция аккумулятора преобразует, шаг за шагом, элементы в вашем (красном) потоке до конечного уменьшенного (зеленого) значения. Функция аккумулятора просто преобразует объект String
в другой String
.
Предположим, что у вас есть те же 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
.
Что если вы не хотите распараллелить свой поток? Ну, комбайнер должен быть предоставлен в любом случае, но он никогда не будет вызван, если не будет получено никаких частичных результатов.
Нет версии reduce , которая принимает два разных типа без комбайнера , поскольку он не может выполняться параллельно (не уверен, почему это требование). Тот факт, что аккумулятора должен быть ассоциативным, делает этот интерфейс практически бесполезным, поскольку:
list.stream().reduce(identity,
accumulator,
combiner);
Производит те же результаты, что и:
list.stream().map(i -> accumulator(identity, i))
.reduce(identity,
combiner);
map
трюк, зависящий от конкретных accumulator
и combiner
, может существенно замедлить работу.
– Tagir Valeev
4 September 2015 в 15:51
accumulator
, отбросив первый параметр.
– quiz123
4 September 2015 в 16:27
mapToInt(String::length)
по сравнению сmapToInt(s -> s.length())
, не уверен, что лучше быть лучше другого, но я предпочитаю, чтобы первый читал. – skiwi 19 June 2014 в 16:35combiner
, почему недостаточно иметьaccumulator
. В этом случае: объединитель необходим только для параллельных потоков, чтобы объединить "накопленный" результаты потоков. – ddekany 24 November 2017 в 15:54