本文共 2896 字,大约阅读时间需要 9 分钟。
Java 8 在2014年3月发布,现在公司内部Java相关的开发(包括服务器端和安卓客户端)所引用的JDK都是jdk1.8,但是几乎项目中没有使用Java 8的特性。
为什么使用Java 8?
目前,不管是服务器还是客户端的CPU都是多核的,而在Java 8 之前的程序只能使用一个核,除非利用多线程才会使用多个内核,而线程使用起来容易出现错误。
Java 8提供了一个新的API(称为“流”,Stream),支持许多数据的并行操作。流式处理的特点,程序中可以从输入流中一个一个读取数据项,然后以同样的方式将数据项写入输出流,一个程序的输出流也可以作为另一个程序的输入流。下面给出一个流式处理的流程图:
上述流图中,你可以把几个基础操作链接起来,来表达复杂的数据处理流水线(在filter后面接上sorted、map和collect操作,这些操作是流式自带的方法),同时代码保持清晰可读。filter的结果被传给sorted方法,再传给map方法,最后传给collect方法。即上述的代码可写为:
ListlowCar = menu.stream() .filter(d->d.getCalories()<400) .sorted(comparing(Dish::getCalories)) .map(Dish::getName) .collect(toList());
由于流式处理的特点使得上一个环节处理完了部分数据,这部分已经处理好的数据可以马上进入下一个环节处理,而不用等所有数据都处理完后再进入下一个环节。
除此之外,流式处理在共享数据不变的情况下,还允许开启并行流模式,加快程序的运行速度。
什么时候用Java 8 流式处理,什么时候不要用?
举个栗子:
对一个List<String> list中的元素进行判断,如果字符串是数字则将字符串转成数字加上1000,再转为数字。
代码如下:
public static void main(String[] args) { StopWatch stopWatch = new StopWatch(); Listlist = new ArrayList<>(); for(int i=0;i<1_000;i++){ list.add("123");list.add("adf");list.add("h12");list.add("345345");list.add("12334"); } //將list中字符串如果是数字则加上1000 //传统的写法 stopWatch.start(); List list2 = new ArrayList<>(); for(String str:list){ if(NumberUtils.isDigits(str)){ int sum = NumberUtils.toInt(str) + 1000; list2.add(String.valueOf(sum)); }else{ list2.add(str); } } stopWatch.stop(); System.out.println(stopWatch.taskCostTime("非java 8")); //java 8的写法 stopWatch.restart(); List list3 = list.stream().map(str->{ if(NumberUtils.isDigits(str)){ int sum = NumberUtils.toInt(str) + 1000; return String.valueOf(sum); }else{ return str; } }).collect(Collectors.toList()); stopWatch.stop(); System.out.println(stopWatch.taskCostTime("java 8"));}
结论分析:
一个统计的表格如下:
上述性能中横坐标是测试的数据集个数,纵坐标是耗费的毫秒数。所有数据都是循环20次求平均值。
可以看到在数据集小于四百多万时,Java 8 并行流性能稍微好于Java 8串行流,好于非Java 8,d当大于六百多万时,反而仍是非Java 8 性能最好。
原因分析:
(1)由于上述字符串转数字加上1000再转回字符串,耗时极短(不到1ms)。
(2)且上述的处理环节只有一个,相对于流式根本没有什么优势。
(3)使用并行流反而效率更低,那是因为并行流的本质上是开启多个线程,而线程的创建以及数据的合并的代价已经超过了(1)中数据处理的代价,因此在数据量一大,这个缺点被极具放大。
为了证明这个观点,增加数字转字符串加上1000花费的时间(Thread.sleep(2)),即该操作会慢2ms。
得到的统计数据表如下:这里非Java 8的性能和Java8串行的性能几乎一致,所以重合。
而Java 8 并行的性能已经远远好于其他两种。
原因分析:为什么Java8串行的性能与非java的性能一致,因为,这里的流的中间操作只有一个,因此没有利用流的特性(至少需要两个),而Java 8的并行流,在处理第一个中间操作开启多线程处理,因此能够显著提高性能,其处理耗时缩短10倍以上。
总结:
Java 8 引入的特性远不止我们上面提到的流式处理,还包括行为参数化、lambda表达式,以及函数式编程等概念。
之所以重点强调流式处理,因为在流式处理中,流的来源可以是文件或者函数生成流, 也可以是集合转化成流,而集合操作是我们在程序中经常使用,并且需要对集合进行遍历等相关业务操作,传统的for循环遍历,不仅需要等全部for循环完成后才能处理下一步,而且在for循环遍历时,只能使用一个核,通过将集合转化成流(通过实际观察,集合转成流本质上是new一个新对象的所需的时间耗时极少),转成流后,就可以充分利用流式处理的特点,显著的提高程序的响应速度,特别对于大数据业务耗时久的集合操作可以提高十倍甚至百倍的响应速度。