前言

看到题目是不是有点疑问:你确定你没搞错?!数组求和???遍历一遍累加起来不就可以了吗???

是的,你说的都对,都听你的,但是我说的就是数组求和,并且我也确实是刚刚学会。╮(╯▽╰)╭

继续看下去吧,或许你的疑问会解开↓

注:记录于学习完《Java 8 实战》数据并行处理与性能,如果有错误,欢迎大佬指正

传统方式

求和方法

我相信你和我一样,提到数组求和,肯定最想想到的就是将数组迭代一遍,累加迭代元素。这是最简单的一种方式,代码实现如下:

public static long traditionSum(long[] arr){//和long sum = 0;//遍历数组中的每个元素for (long l : arr) {//累加sum += l;}return sum;}

性能测试方法

为了便于我们测试性能,我们写一个比较通用的测试函数,用来记录对每种方式的运行时间,直接看代码吧!

public static long test(Function<long[], Long> function, long[] arr){//记录最快的时间long fasttime = Long.MAX_VALUE;//对函数调用10次for (int i = 0; i < 10; i++) {//记录开始的系统时间long start = System.nanoTime();//执行函数long result = function.apply(arr);//获取运行时间转换为mslong time = (System.nanoTime() - start) / 1_000_000;//打印本次的就和结果System.out.println("结果为:" + result);//更新最快的时间if (time < fasttime) {fasttime = time;}}return fasttime;}

性能测试代码解释

  • 传入参数Function<long[], Long> function: 我们需要测试的函数,稍后我们会把每种求和方式都传入到这个参数里面。如果你对java 8的新特性(Lambda表达式、行为参数化、方法引用等)不熟悉,那么你可以理解为Function是一个匿名类,我们传入的求和方法会放到function.apply()的方法中,我们调用apply()方法,实际上就是调用我们传入的求和方法。
  • Function<long[], Long>的泛型: 第一个为我们求和方法需要传入的参数的类型(传入一个long类型的数组作为待求和数组),第二个为我们的求和方法返回值的类型(返回数组的和为long)
  • long[] arr:待求和数组
  • 关于为什么会调用10次:任何的Java代码都需要多执行几次才会被JIT编译器优化,多执行几次是为了保证我们测量性能的准确性。

数据准备

方法有了,我们当然要准备好我们的测试数据了,为了简便起见,我们直接顺序生成1到100,000,000(1亿)来最为待求和的数组:

long[] longs = LongStream.rangeClosed(1, 100_000_000).toArray();

测试性能

数据有了,我们可以测试一下传统方式的性能了(所在类TestArraysSum)

public static void main(String[] args) {long[] longs = LongStream.rangeClosed(1, 100_000_000).toArray();//执行测试函数long time = test(TestArraysSum::traditionSum, longs);System.out.println("时间为: " + time + "ms");}

结果:

结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
时间为: 62ms

继续看其他方式

Stream流的顺序执行方式

求和方法

java 8的流可谓是非常的强大,配合lambda表达式和方法引用,极大的简化了对数据处理方面,下面是使用流对数组进行顺序求和

public static long sequentialSum(long[] arr){return Arrays.stream(arr).reduce(0L, Long::sum);}

代码解释

  • Arrays.stream(arr)将我们传入的数组变为一个流(此处没有Java包装类与原始类型的装箱和拆箱,装箱和拆箱会极大影响性能,应该尽量避免)
  • .reduce(0L, Long::sum):0L是初始值,Long::sum通过方法引用的方式使用Long提供的求和函数,对数组的每一个元素都进行求和

性能测试

Java 8让我们的代码极大的简化了,那么性能如何呢?
我们将main方法内执行求和方法部分换为调用这个方法看看

long time = test(TestArraysSum::sequentialSum, longs);
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
时间为: 62ms

emmmm 好像差不多,Ծ‸Ծ,先不急,Java 8的流给我们带来的另一大好处还没用上呢,下面我们就来看看吧

Stream流的并行执行

求和方法

Java 8 的Stream流可以让我们非常简单的去使用多线程解决问题,而我们的求和需求好像完美适合多线程问题去解决

public static long parallelSum(long[] arr){return Arrays.stream(arr).parallel().reduce(0L, Long::sum);}

代码解释

  • .parallel():与顺序流实现相比,仅仅是多调用了一个parallel()方法,他的作用就是将顺序流转化为并行流(其实就是改变了一下boolean标志),如何并行执行呢,不用我们实现,无脑调用就好了

性能测试

long time = test(TestArraysSum::parallelSum, longs);

结果

结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
时间为: 52ms

哦吼~这就很舒服了,是不是瞬间就快了

注:并行流内部默认使用ForkJoinPool的线程池,线程数量默认为计算机处理器的数量,使用Runtime.getRuntime().availableProcessors()可以获取处理器核心数
(我的测试环境是8个),可是设置这个值,但是只能全局设置,所以最好还是不要更改

是不是疑问我们除了调用parallel()方法以外什么都没干,究竟是怎么实现多线程的呢,其实并行流底层使用的是Java 7的分支/合并框架,下面我们就看一下使用分支/合并框架实现多线程求和吧!

分支合并框架的实现方式

分支合并框架的目的是以递归的方式将可以并行的任务拆分成更小的子任务,然后将每个子任务的结果进行合并生成整体结果。

求和方法

我们可以继承RecursiveTask实现其compute()方法
分支合并实现的类ForkJoinSumCalculator

package java_8.sum;import java.util.concurrent.RecursiveTask;public class ForkJoinSumCalculator extends RecursiveTask<Long> {//任务处理的数组private final long[] arr;//当前任务处理的开始和结束索引private final int start;private final int end;//划分到处理数组的长度10_000_000变不来划分,进而合并public static final long THRESHOLD = 10_000_000;//公共的构造函数,用来创建主任务public ForkJoinSumCalculator(long[] arr){this(arr,0,arr.length);}//私有的构造函数,用来创建子任务private ForkJoinSumCalculator(long[] arr, int start, int end){this.arr = arr;this.start = start;this.end = end;}//实现的方法@Overrideprotected Long compute() {//当时子任务处理长度int length = end - start;//当数组处理长度足够小时if (length <= THRESHOLD){//进行合并return computeSequentially();}//创建第1个子任务对前面一半数组进行求和ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(arr, start, start + length / 2);//使用线程池中的另一个线程求和前一半leftTask.fork();//创建第2个子任务对后一半数组进行求和ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(arr, start + length / 2, end);//直接使用当前线程进行求和 获取求和结果Long rightResult = rightTask.compute();//获取前一半的求和结果Long leftTesult = leftTask.join();//合并return leftTesult + rightResult;}//合并是的调用方法 迭代求和private long computeSequentially(){long sum = 0;for (int i = start; i < end; i++) {sum += arr[i];}return sum;}
}

public static final long THRESHOLD = 10_000_000;
划分的界线使我随便设定的当前值的情况下会划分为10个线程

然后我们就可以编写我们的求和方法了

    public static long forkJoinSum(long[] arr){ForkJoinSumCalculator calculator = new ForkJoinSumCalculator(arr);return new ForkJoinPool().invoke(calculator);}

性能测试

long time = test(TestArraysSum::forkJoinSum, longs);

结果:

结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
结果为:5000000050000000
时间为: 53ms

还不错,跟并行流的性能差不多

由于分支合并时的递归调用也消耗性能,因此我们更改public static final long THRESHOLD = 10_000_000;的大小时,运行时间会差距很大。
具体更改多少效率最高,这个真的不好说

总结

  • 使用了4种方式完成数组求和
  • 使用传统方式(遍历)效率其实也不低,因为实现方式比较接近底层
  • 使用流极大简化了数组处理
  • 并行流在适合的场景下可以大展身手
  • 并行流使用分支合并框架实现

个人公众号

我的个人公众号已经开通了,正在按计划建设中,以后的文章会第一时间发布在公众号中,同时也给大家准备了亿点学习资料,关注公众号后台发送:学习资料

感动,我终于学会了用Java对数组求和相关推荐

  1. 【Java】数组求和

    [Java]数组求和 需求 键盘录入5个整数,存储到数组中,并对数组求和 代码 package com.cxl.demo24; /* 数组求和 需求:键盘录入5个整数,存储到数组中,并对数组求和*/i ...

  2. java 数组怎么求和,感动,我终于学会了Java对数组求和

    前言 看到题目是不是有点疑问:你确定你没搞错?!数组求和???遍历一遍累加起来不就可以了吗??? 是的,你说的都对,都听你的,但是我说的就是数组求和,并且我也确实是刚刚学会.╮(╯▽╰)╭ 继续看下去 ...

  3. java 递归数组求和,使用递归实现数组求和示例分享

    使用递归实现数组求和示例分享 思路如下: 给定一个含有n个元素的整型数组a,求a中所有元素的和.问题的难点在于如何使用递归上.如果使用递归,则需要考虑如何进行递归执行的开始以及终止条件,首先如果数组元 ...

  4. java求数组和值_用java编写数组求和,array[]和ArrayList()?

    大家好,在这里想请问大家一个java问题,具体如下: 程序1 public class A11 { public static void main(String args[]) { int a[]={ ...

  5. Java 递归数组求和

    private static Double sumArr(String arr[], int arrSize) {if(arrSize == 1) {return Double.parseDouble ...

  6. 从一个OutOfMemoryError 学会了分析Java内存泄漏问题

    从一个OutOfMemoryError 学会了分析Java内存泄漏问题 以前都是好好的,最近出现了 oom. 问题 开始是: java.lang.OutOfMemoryError: Java heap ...

  7. 学会下载安装JAVA

    学会下载安装Java 一.下载JDK8U231 (一).从QQ群里下载JDK-8U231-windows-x64.exe (二).JDK8U231[百度](https://cn.bing.com/se ...

  8. 常在河边湿鞋系列---终于见到真正的Java人了

    Java不仅仅是一门编程语言,也是印度尼西亚的一个岛屿名, 学Java,Java人,终于见到了真的Java人

  9. 用计算机控制着挖掘机炒菜图片大全,搞笑GIF:终于学会了用计算机控制挖掘机炒菜了,不知道味道咋样...

    原标题:搞笑GIF:终于学会了用计算机控制挖掘机炒菜了,不知道味道咋样 终于学会了用计算机控制挖掘机炒菜了,不知道味道咋样 驴:终于拖到一次妹子,驴生值了 女朋友生气了,非要说我买的这个山头不如那面那 ...

最新文章

  1. 为什么阿里P8、P9技术大牛反复强调“结构化思维”?
  2. android 多用户管理UserManager
  3. 数据中台全景架构及模块解析
  4. python enumerate用法_【Python】python enumerate用法总结
  5. GitHub入门:如何上传与下载工程?
  6. 各种操作系统简介和功能分析
  7. c 站点下html页面拦截器,HTML内容拦截器「HTML Content Blocker」
  8. vivado fpga最最简单的入门--led闪烁 创建工程+代码输入+添加引脚约束完整具体流程
  9. c位边上还有什么位_会议桌C位,是它!
  10. python使用缩进来体现-Python使用缩进来体现代码之间的逻辑关系。
  11. 孙子兵法吃鸡之地形篇
  12. html 目录生成器,Tocify:动态节点目录菜单生成器_html/css_WEB-ITnose
  13. 单片机内存及运行原理
  14. linux c语言内存管理,C程序内存管理 第2页_Linux编程_Linux公社-Linux系统门户网站...
  15. 谷歌工具栏不再支持火狐浏览器
  16. 认识UDS诊断29认证服务-Authentication Service
  17. iOS CocoaPods 详解
  18. 用于欺诈检测的综合金融数据集
  19. MATLAB的figure文件保存成高清图片的技巧01
  20. NPDP产品经理小知识-创意工具(Scamper)

热门文章

  1. 上市公司杰创智能携手甄云,启动供应链采购数字化升级
  2. CSS - 制作三角形
  3. 再传捷报!人大金仓获2022年中国信创产业拳头奖“最佳数据库品牌”大奖
  4. 知到网课礼仪文化修养考试题库|真题试题(含答案)
  5. 822D My pretty girl Noora
  6. 停车场车牌识别收费系统厂家
  7. 搭建内网DNS服务器教程
  8. 鸿蒙OS原子化服务卡片原理和架构分析
  9. 液化气瓶爆炸总发生,要为监管敲警钟
  10. 基于SpringBoot的家具销售管理系统