Пусть в мире не будет сложной Java для изучения
Нравится, Следуй, Избранное
1. Введение
-
что такое уменьшить? Короче говоря, я думаю о редукции как о нормализованной итерационной операции. Берет поток и объединяет их в простой результат, многократно применяя операции.
-
Если вы хотите сравнить его с collect, обычно collect возвращает List
, Set , Map ..., тогда как reduce обычно возвращает только T (но T является универсальным, на самом деле вы можете возвращать любой тип, включая Список в классе). -
В этой статье в основном представлена функция reduce() с тремя параметрами: parallel, non-thread-safe и Combiner.
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity,BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U,? super T,U> accumulator,BinaryOperator<U> combiner);
2, небольшой тестовый нож
- сократить одним аргументом
- Среди них нижние уровни, такие как sum, min и max, реализованы с помощью сокращения.
/***
* @param: accumulator
* @return: Optional
*/
Optional<T> reduce(BinaryOperator<T> accumulator);
- Среди них BinaryOperator
можно рассматривать как частный случай BiFunction .
public class Main {
public static void main(String[] args) {
List<Integer> list = Lists.newArrayList(1,2,3,4,5);
list.stream().reduce(
new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}));
//=====等价于=====
System.out.println(IntStream.range(1, 100).reduce((v1, v2) -> v1 + v2).orElse(0));
//=====等价于=====
System.out.println(IntStream.range(1, 100).reduce(Integer::sum).orElse(0));
}
}
integer=1===integer2=2
integer=3===integer2=3
integer=6===integer2=4
integer=10===integer2=5
3. Продемонстрируйте свои навыки
- Сокращение двух параметров на самом деле на одно начальное значение больше, чем на один параметр. То есть первый параметр сокращения указывается для первой итерации.
public class Main {
public static void main(String[] args) {
List<Integer> list = Lists.newArrayList(1,2,3,4,5);
//初始值100
list.stream().reduce(
100,
new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
System.out.println("integer="+integer+"===integer2="+integer2);
return integer + integer2;
}
}));
}
}
//初始值是100,不是1。比没有初始值的reduce多迭代1次。
integer=100===integer2=1
integer=101===integer2=2
integer=103===integer2=3
integer=106===integer2=4
integer=110===integer2=5
4. Расправить крылья и парить
- Первые два — это миньоны, а снижение на три параметра — это большой БОСС.
- Позвольте мне сначала сделать вывод, третий параметр бесполезен при непараллельном потоке. Когда в потоке только один элемент, распараллеливание не будет выполняться, даже если указано parallel.
- При использовании сокращения с тремя параметрами обязательно обратите внимание на безопасность потоков.
<U> U reduce(U identity,BiFunction<U,? super T,U> accumulator,BinaryOperator<U> combiner);
- Краткое понимание BiFunction, первый параметр T, второй параметр U, возвращаемое значение R
=====> (T t,U u)-> (R)r
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
- Взгляните на демо ниже (рекомендуется потратить несколько минут на размышления, возможно, вы все поймете)
public static void main(String[] args) {
ArrayList<Integer> accResult = Stream.of(1, 3, 5, 7)
.reduce(new ArrayList<>(),
new BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>>() {
@Override
public ArrayList<Integer> apply(ArrayList<Integer> integers, Integer item) {
System.out.println("before add: " + integers);
System.out.println("item= " + item);
integers.add(item);
System.out.println("after add : " + integers);
System.out.println("In BiFunction");
return integers;
}
}, new BinaryOperator<ArrayList<Integer>>() {
@Override
public ArrayList<Integer> apply(ArrayList<Integer> integers,
ArrayList<Integer> integers2) {
integers.addAll(integers2);
System.out.println("integers: " + integers);
System.out.println("integers2: " + integers2);
System.out.println("In BinaryOperator");
return integers;
}
});
System.out.println("accResult: " + accResult);
}
- Содержимое третьего параметра вообще не печатается? Тогда зачем вам третий параметр, не переживайте
public static void main(String[] args) {
ArrayList<Integer> accResult = Stream.of(1, 3, 5, 7).parallel()
.reduce(new ArrayList<>(),
new BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>>() {
@Override
public ArrayList<Integer> apply(ArrayList<Integer> integers, Integer item) {
integers.add(item);
return integers;
}
}, new BinaryOperator<ArrayList<Integer>>() {
@Override
public ArrayList<Integer> apply(ArrayList<Integer> integers,
ArrayList<Integer> integers2) {
integers.addAll(integers2);
System.out.println("thread name="+Thread.currentThread().getName()+" ==== integers=" + integers);
System.out.println("integers2: " + integers2);
System.out.println("In BinaryOperator");
return integers;
}
});
//打印结果几乎每次都不同
System.out.println("accResult: " + accResult);
}
- При еще одной параллели результат печати почти каждый раз разный, да и нулей куча. Данные не полные, мои 1, 3, 5 и 7 неполные. Иногда сообщают об исключениях.
5. Сокращение под параллельным потоком
- Проблемы безопасности потоков возникают при использовании параллельных потоков. Например, ArrayList в приведенном выше примере не является потокобезопасным классом, и многие операции могут привести к неожиданным результатам.
5.1. Параллельное уменьшить под основными типами данных
public static void main(String[] args) {
System.out.println(
Stream.of(1, 2, 3, 4).parallel().reduce(5, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}
, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}));
}
- Принципиальная схема выглядит следующим образом: (где количество накопителей = потоку, количество объединителей на 1 меньше, чем количество накопителей)
5.2 Если первым параметром является объект, такой как ArrayList, а не базовый тип данных или String, результат может сильно отличаться от того, что мы думаем.
-
Результаты печати и имена потоков, скорее всего, будут несогласованными, а ArrayList не является потокобезопасным. (Если Collections.synchronizedList(a) оборачивает элементы, меньше не будет).
-
Сосредоточьтесь на System.out.println(acc==item);. Второй параметр каждого аккумулятора совпадает с ArrayList в первом параметре.
-
Если вы преобразуете List
a в Collections.synchronizedList(a), порядок может быть другим, но элементы должны быть завершены.
//Приблизительная схема
6. Резюме
- Кратко представлены три перегруженные функции сокращения, и особое внимание уделяется сокращению с тремя параметрами.
- Аккумулятор (третий параметр) бесполезен в непараллельных потоках, количество аккумуляторов в параллельных потоках равно количеству элементов потока, а количество объединителей на единицу меньше числа элементов потока.
- Потокобезопасность при параллельном потоке. Первый параметр в объединителе — это разница между типами объектов, такими как ArrayList, и базовыми типами.