问题:计算数字的累加如何才能更快?
一、计算方式比较
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
}
参考来源: