Let the world have no difficult to learn Java

Like, follow, favorites

1, the preface

  • What is reduce? Simply speaking, I think reduce is a normalized iterative operation. Take a stream and combine them into a simple result by repeatedly applying the operations.

  • Collect returns List<T>, Set<T>, Map<T>… Reduce usually only returns T (but T is generic, you can actually return any type including List in the class).

  • This article mainly introduces reduce () with three parameters, parallel, non-thread-safe, and 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);

Copy the code

2. Test the cat

  • Reduce of one parameter
  • Sum, min, Max and other bottom layers are realized by Reduce.
/ * * * *@param: accumulator
* @return:  Optional
*/
Optional<T> reduce(BinaryOperator<T> accumulator);
Copy the code
  • The BinaryOperator

    can be regarded as a special case of BiFunction

    .
    ,t,t>
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) {
            returninteger + integer2; }}));//===== is equivalent to =====
    System.out.println(IntStream.range(1.100).reduce((v1, v2) -> v1 + v2).orElse(0));
  //===== is equivalent to =====
    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
Copy the code

3. Flex your muscles

  • A two-parameter reduce is actually one more initial value than one parameter. That is, the first parameter in the first iteration of Reduce is specified.
public class Main {

  public static void main(String[] args) {

    List<Integer> list = Lists.newArrayList(1.2.3.4.5);
    
    // The initial value is 100
    list.stream().reduce(
        100.new BinaryOperator<Integer>() {
          @Override
          public Integer apply(Integer integer, Integer integer2) {
            System.out.println("integer="+integer+"===integer2="+integer2);
            returninteger + integer2; }})); }}// The initial value is 100, not 1. One more iteration than reduce with no initial value.
integer=100===integer2=1
integer=101===integer2=2
integer=103===integer2=3
integer=106===integer2=4
integer=110===integer2=5
Copy the code

Spread your wings and soar

  • The first two are all underdogs, and the three-parameter Reduce is the big BOSS.
  • First of all, the third parameter is useless for non-parallel flow. If there is only one element in the stream, even if parallel is specified, parallel will not run.
  • When using three-parameter reduce, it is important to be thread-safe.
<U> U  reduce(U identity,BiFunction<U,? super T,U> accumulator,BinaryOperator<U> combiner);

Copy the code
  • BiFunction, the first parameter T, the second parameter U, returns R
=====> (T t,U u)-> (R)r  
Copy the code
@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)); }}Copy the code
  • Let’s see a demo (take a few minutes to think about it, maybe you’ll understand it completely).
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");
                returnintegers; }},new BinaryOperator<ArrayList<Integer>>() {
              @Override
              public ArrayList<Integer> apply(ArrayList
       
         integers, ArrayList
        
          integers2)
        
        {
                integers.addAll(integers2);
                System.out.println("integers: " + integers);
                System.out.println("integers2: " + integers2);
                System.out.println("In BinaryOperator");
                returnintegers; }}); System.out.println("accResult: " + accResult);
  }
Copy the code
  • The third parameter is not printed at all? Why do you need a third parameter? Don’t worry
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);
                returnintegers; }},new BinaryOperator<ArrayList<Integer>>() {
              @Override
              public ArrayList<Integer> apply(ArrayList
       
         integers, ArrayList
        
          integers2)
        
        {
                integers.addAll(integers2);
                System.out.println("thread name="+Thread.currentThread().getName()+" ==== integers=" + integers);
                System.out.println("integers2: " + integers2);
                System.out.println("In BinaryOperator");
                returnintegers; }});// Prints different results almost every time
    System.out.println("accResult: " + accResult);
  }
Copy the code
  • Parallel, print results almost different every time, and a bunch of null. The data is also incomplete, my 1,3,5,7 are incomplete. Sometimes there are exceptions.

5. Reduce in parallel flow

  • Thread safety issues arise with concurrent flow. ArrayList, for example, is a non-thread-safe class, and many operations can lead to unexpected results.

5.1 Parallel Reduce for basic data types

 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) {
                returninteger + integer2; }},new BinaryOperator<Integer>() {
              @Override
              public Integer apply(Integer integer, Integer integer2) {
                returninteger + integer2; }})); }Copy the code
  • Accumulator =stream, and the number of combiner is 1 less than accumulator.

5.2. If the first argument is an object such as ArrayList rather than a primitive data type or String, the result may be quite different from what we expect.

  • Print results, thread names will probably not be inconsistent, and ArrayList is not thread-safe. (if the Collections. SynchronizedList under (a) the packing element is not less).

  • Focus on the System. The out. Println (acc = = item); . The second parameter of each Accumulator is the same as the ArrayList in the first parameter.

  • If the List < Integer > a into Collections. SynchronizedList (a), then the order is not necessarily consistent, but the elements must be full.

// Outline of the diagram

6, summary

  • Three overloaded functions of reduce are briefly introduced, and reduce with three parameters is emphasized.
  • Accumulator (the third parameter) is not used for non-parallel flow. The number of accumulator in parallel flow is the same as that of stream elements, and the number of combiners is one less than that of stream elements.
  • The first parameter of Combiner is ArrayList and other object types and the differences between the basic types are explained.