问题:计算数字的累加如何才能更快?

一、计算方式比较

1. for循环

public static void test1(){ 
    Long sum = 0L;
    long start = System.currentTimeMillis(); 
    for (Long i = 1L; i <= 10_0000_0000; i++) {
    	sum += i; 
    }
    long end = System.currentTimeMillis();
    System.out.println("sum="+sum+" 时间:"+(end-start)); 
}

2. ForkJoin

public static void test2() throws ExecutionException, InterruptedException { 
    long start = System.currentTimeMillis();
    ForkJoinPool forkJoinPool = new ForkJoinPool();
    ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
    ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任务 
    Long sum = submit.get();
    long end = System.currentTimeMillis();
    System.out.println("sum="+sum+" 时间:"+(end-start)); 
}
public class ForkJoinDemo extends RecursiveTask<Long> {

    private Long start;
    private Long end;

    // 临界值
    private Long temp = 10000L;

    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }

    // 计算方法
    @Override
    protected Long compute() {
        if ((end-start)<temp){
            Long sum = 0L;
            for (Long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }else { // forkjoin 递归
            long middle = (start + end) / 2; // 中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork(); // 拆分任务,把任务压入线程队列
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
            task2.fork(); // 拆分任务,把任务压入线程队列

            return task1.join() + task2.join();
        }
    }
}

3. 并行流

public static void test3(){
    long start = System.currentTimeMillis();
    // Stream并行流 ()  (]
    long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
    long end = System.currentTimeMillis();
    System.out.println("sum="+"时间:"+(end-start));
}

二、并行流

1. reduce

在 Java 8 中,Stream.reduce()合并流的元素并产生单个值。

使用 for 循环的简单求和运算、相当于 Stream.reduce()

  int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  // 1st argument, init value = 0
  int sum = Arrays.stream(numbers).reduce(0, (a, b) -> a + b);

  System.out.println("sum : " + sum); // 55

或方法引用 Integer::sum

int sum = Arrays.stream(numbers).reduce(0, Integer::sum); // 55

Integer.java

/**
 * Adds two integers together as per the + operator.
 *
 * @param a the first operand
 * @param b the second operand
 * @return the sum of {@code a} and {@code b}
 * @see java.util.function.BinaryOperator
 * @since 1.8
 */
public static int sum(int a, int b) {
    return a + b;
}

2. reduce方法签名

Stream.java

T reduce(T identity, BinaryOperator<T> accumulator);

IntStream.java

int reduce(int identity, IntBinaryOperator op);

LongStream.java

long reduce(int identity, LongBinaryOperator op);
  • identity = 默认值或初始值。

  • BinaryOperator = 函数式接口,取两个值并产生一个新值。(注: java Function 函数中的 BinaryOperator 接口用于执行 lambda 表达式并返回一个 T 类型的返回值)

  • 如果缺少identity参数,则没有默认值或初始值,并且它返回 optional。

Stream.java

Optional<T> reduce(BinaryOperator<T> accumulator);

3. range

range,需要传入开始节点和结束节点两个参数,返回的是一个有序的LongStream。包含开始节点和结束节点两个参数之间所有的参数,间隔为1。用数学区间可以表示为[ startInclusive,endExclusive)

示例一

public class LongStreamExample {
    public static void main(String[] args) {
        //计算1到10累加的结果
        //long sum = LongStream.range(0L, 11).reduce(0,(a,b)->a+b);
        long sum = LongStream.range(0L, 11).reduce(0, Long::sum);
        System.out.println(sum);
    }
}
//结果:55

rangeClosed

rang 差别就是rangeClosed包含最后的结束节点,range不包含。

用数学区间可以表示为[startInclusive,endExclusive]

示例

public class LongStreamExample {
    public static void main(String[] args) {
        //计算1到10累加的结果
        long sum = LongStream.rangeClosed(0L, 10).reduce(0, Long::sum);
        System.out.println(sum);
    }
}
//结果55

4. parallel

并行流:简单的理解就是通过并发来提升效率。

import java.util.stream.LongStream;

public class LongStreamExample {
    public static void main(String[] args) {
        test();
        test2();
    }
    public static void test(){
        long star = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum);
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("使用parallel流运行时间:" + (end - star));
    }
    public static void test2(){
        long star = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(0L,10_0000_0000L).reduce(0,Long::sum);
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("不使用parallel流运行时间:" + (end - star));
    }
}
500000000500000000
使用parallel流运行时间:543
500000000500000000
不使用parallel流运行时间:1698

三、更多例子

1. 数学运算。

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int sum = Arrays.stream(numbers).reduce(0, (a, b) -> a + b);    // 55
int sum2 = Arrays.stream(numbers).reduce(0, Integer::sum);      // 55

int sum3 = Arrays.stream(numbers).reduce(0, (a, b) -> a - b);   // -55
int sum4 = Arrays.stream(numbers).reduce(0, (a, b) -> a * b);   // 0, initial is 0, 0 * whatever = 0
int sum5 = Arrays.stream(numbers).reduce(0, (a, b) -> a / b);   // 0

2. 最大和最小

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int max = Arrays.stream(numbers).reduce(0, (a, b) -> a > b ? a : b);  // 10
int max1 = Arrays.stream(numbers).reduce(0, Integer::max);            // 10

int min = Arrays.stream(numbers).reduce(0, (a, b) -> a < b ? a : b);  // 0
int min1 = Arrays.stream(numbers).reduce(0, Integer::min);            // 0

3. 连接字符串。

  String[] strings = {"a", "b", "c", "d", "e"};

  // |a|b|c|d|e , the initial | join is not what we want
  String reduce = Arrays.stream(strings).reduce("", (a, b) -> a + "|" + b);

  // a|b|c|d|e, filter the initial "" empty string
  String reduce2 = Arrays.stream(strings).reduce("", (a, b) -> {
      if (!"".equals(a)) {
          return a + "|" + b;
      } else {
          return b;
      }
  });

  // a|b|c|d|e , better uses the Java 8 String.join :)  (最好使用 Java 8 的 String.join)
  String join = String.join("|", strings);

四、Map & Reduce

一个简单的 map 和 reduce 示例,用于从发票 List 中求 BigDecimal 的和。

JavaReduce.java

package com.mkyong;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;

public class JavaReduce {

    public static void main(String[] args) {
        // 发票集合
        List<Invoice> invoices = Arrays.asList(
                new Invoice("A01", BigDecimal.valueOf(9.99), BigDecimal.valueOf(1)),
                new Invoice("A02", BigDecimal.valueOf(19.99), BigDecimal.valueOf(1.5)),
                new Invoice("A03", BigDecimal.valueOf(4.99), BigDecimal.valueOf(2))
        );

        BigDecimal sum = invoices.stream()
                .map(x -> x.getQty().multiply(x.getPrice()))    // map,对集合中的元素进行操作
                .reduce(BigDecimal.ZERO, BigDecimal::add);      // reduce,将上一步得到的结果进行合并得到最终的结果

        System.out.println(sum);    // 49.955
        System.out.println(sum.setScale(2, RoundingMode.HALF_UP));  // 49.96 使用setScale方法进行四舍五入
    }
}

class Invoice {

    // 发票号码
    String invoiceNo;
    // 价格
    BigDecimal price;
    // 数量
    BigDecimal qty;

    // getters, stters n constructor
}

参考来源:

https://www.cnblogs.com/lanxinren/p/14715796.html

https://www.cnblogs.com/gaohanghang/p/12390233.html

https://mkyong.com/java8/java-8-stream-reduce-examples/