JAVA8给我带了什么——并行流和接口新功能
流,确定是笔者内心很向往的天堂,有他之后JAVA在处理数据就变更加的灵动。加上lambda表达不喜欢都不行。JAVA8也为流在提供另一个功能——并行流。即是有并行流,那么是不是也有顺序流。没有错。我前面操作的一般都是顺序流。在JAVA8里面并行流和顺序流是可以转变的。来看一个例子——笔者打印数字。
package com.aomi; import java.util.stream.LongStream; public class Main { public static void main(String[] args) {
// TODO Auto-generated method stub LongStream.range(, ).forEach(i -> {
System.out.print(i + " ");
});
} }
LongStream.range这个方法是来获取数字的。这里表示获得0到10,但不含10 的数字。运行结果:
现在让我们把他换成并行来看看。
package com.aomi; import java.util.stream.LongStream; public class Main { public static void main(String[] args) {
// TODO Auto-generated method stub LongStream.range(, ).parallel().forEach(i -> {
System.out.print(i + " ");
});
} }
运行结果:
俩个结果相比一下,我们就可以明显他们发生了变化。我们只是加一个parallel函数就发生好多的变化。笔者本来是要讲他们之间的性能比较的。不敢,因为笔者试好还有个例子。却发现有时候顺序流都比并行流来快。上面是顺序流转并行流。在来看一下相反的。
public static void main(String[] args) {
// TODO Auto-generated method stub List<Integer> datas = Arrays.asList(1,2,3,4,56); datas.parallelStream().forEach(i -> {
System.out.print(i + " ");
});
}
parallelStream函数就是用来建一个并行流的。运行结果:
转为顺序流
public static void main(String[] args) {
// TODO Auto-generated method stub List<Integer> datas = Arrays.asList(1,2,3,4,56); datas.parallelStream().sequential().forEach(i -> {
System.out.print(i + " ");
});
}
运行结果:
我们都知道流里面用到了JAVA7里面的分支和合并的框架来进行的。古代有一个词叫分而治之。把一个事情分为几个小事件。然面各自处理。所以了解代码里面是什么样子折分成小事件是非常重要的。他有俩个关键字Fork和Join。Fork方法你可以理解为拆分,并压入线程队列中。而Join就是合并的意思了。来笔者来写一个试。
package com.aomi; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask; public class DistinctCharForkJoin extends RecursiveTask<List<Character>> { private List<Character> chars; public DistinctCharForkJoin(List<Character> chars) {
this(chars, 0, chars.size());
} public DistinctCharForkJoin(List<Character> chars, int start, int end) { this.chars = chars.subList(start, end);
} @Override
protected List<Character> compute() {
// TODO Auto-generated method stub
List<Character> tmpChars = new ArrayList<Character>(); // 判断不可以在拆分了
if (this.chars.size() < 3) { for (Character character : chars) {
if (!tmpChars.contains(character))
tmpChars.add(character);
} } else {// 表示可以在拆分。 int len = this.chars.size(); // 建立左边的小事件
DistinctCharForkJoin leftForkJoin = new DistinctCharForkJoin(chars, 0, len / 2); leftForkJoin.fork(); // 建立右边的小事件
DistinctCharForkJoin rightForkJoin = new DistinctCharForkJoin(chars, len / 2, len); rightForkJoin.fork(); List<Character> rChars = rightForkJoin.join(); List<Character> lChars = leftForkJoin.join(); // 俩个合并。
for (Character character : rChars) {
if (!tmpChars.contains(character))
tmpChars.add(character);
} for (Character character : lChars) {
if (!tmpChars.contains(character))
tmpChars.add(character);
} } return tmpChars;
} }
Main:
public static void main(String[] args) {
// TODO Auto-generated method stub List<Character> chars = Arrays.asList('a', 'b', 'c', 'd', 'b', 'a'); DistinctCharForkJoin task = new DistinctCharForkJoin("main", chars); List<Character> resChars = new ForkJoinPool().invoke(task); for (Character character : resChars) { System.out.print(character +" ");
}
}
运行结果:
你们一定很奇怪为什么笔者会讲到JAVA7带来的东西呢?JAVA8引入了一个新的接口——Spliterator接口。人称可分迭代器。如果你有心去看一个接口List的话,你可能会发现一个方法。如下
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED);
}
Spliterator接口:
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
long estimateSize();
int characteristics();
}
讲JAVA7里面的分支/合并的目地就是为了理解Spliterator接口的作用。如下
- tryAdvance:用于遍历当前的元素。如果还有的话,就返回true;
- trySplit:用于拆分。如果当前不可以在拆分的话,就返回null;跟上面的compute方法很像。
- estimateSize:表示还需要遍历的元素有多少。
- characteristics:表示当前处理的数据是什么样子的。比如是否有序,每一元素是否为null。上面Spliterator接口的代码是笔者去掉大部分复制出来。这个值都在代码中。作用你们可以自己去看一下代码就是知道。
要注意Spliterator接口只是用去拆分任务的作用。JAVA8帮你做了很多拆分的功能。大部分你可以不用自己写。当然如果你想要自己动手。你只要实现这样子就可以了。如下
package com.aomi; import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer; public class DistinctCharSpliterator implements Spliterator<Character> { private List<Character> chars;
private int index = 0; public DistinctCharSpliterator(List<Character> chars) {
this.chars = chars;
} public DistinctCharSpliterator(List<Character> chars, int start, int end) {
this.chars = chars.subList(start, end);
} @Override
public boolean tryAdvance(Consumer<? super Character> action) {
// TODO Auto-generated method stub
action.accept(this.chars.get(index++));
return index < this.chars.size();
} @Override
public Spliterator<Character> trySplit() {
// TODO Auto-generated method stub
int difLen = this.chars.size() - index; // 判断不可以在拆分了
if (difLen < 3) {
return null;
} else {// 表示可以在拆分。 DistinctCharSpliterator spliterator = new DistinctCharSpliterator(chars.subList(index, index + 2)); index = index + 2; return spliterator; }
} @Override
public long estimateSize() {
// TODO Auto-generated method stub
return this.chars.size() - index;
} @Override
public int characteristics() {
// TODO Auto-generated method stub
// 有序 元素不空 遍历过程不能删除,和修改 增加
return ORDERED + NONNULL + IMMUTABLE;
} }
Main:
public static void main(String[] args) {
// TODO Auto-generated method stub List<Character> chars = Arrays.asList('a', 'b', 'c', 'd', 'b', 'a'); DistinctCharSpliterator distinctCharSpliterator = new DistinctCharSpliterator(chars); Stream<Character> stream = StreamSupport.stream(distinctCharSpliterator, true); stream.distinct().forEach((Character ch) -> { System.out.print(ch+" ");
}); }
运行结果:
上面的例子有一点烂。但是大家可以复制做一下继点去看看他的执行过程。就可以看出很多东西来。主要是理解这个原理就可以了。
流的并行功能并没有让笔者有多心动。真正让笔者感觉不错的要属于JAVA8对接口的升级。什么意思?笔者不清楚有多少个人写个框架或是读过框架源码,一般框架里面都会用到一些面向接口的编程模式。那个或多或少会有这样子感觉。一但项目发布出去,这个时候你想要修改接口。比如在接口里面增加一个新的功能方法。这样子时候你就不得不考虑一下外面有多少个人在实现你现在框架的接口。因为你增加一个接口的新方法。别人也要跟着实现,不然的一定会报错或是运行时候报错。不管哪一种都是设计者不想看到的。
JAVA8现在可以让你定义接口的默认方法。什么思意呢?让笔得写一个例子。
Base接口:
package com.aomi; public interface Base {
void call();
}
BaseA类:
package com.aomi; public class BaseA implements Base { @Override
public void call() { } }
Main:
package com.aomi; public class Main { public static void main(String[] args) {
// TODO Auto-generated method stub Base baseA = new BaseA(); baseA.call();
}
}
上面的代码没有什么特别的。现在笔者在加一个方法。看一个他会不会有问题。如下
base类:
package com.aomi; public interface Base {
void call();
void call2();
}
结果:
看到吧。BaseA类马上就报错。现在笔者在加上一个默认的方法会什么呢?
package com.aomi; public interface Base {
void call(); default void call2() {
System.out.println("default call2");
}
}
Main修改一下吧。
package com.aomi; public class Main { public static void main(String[] args) {
// TODO Auto-generated method stub Base baseA = new BaseA(); baseA.call2();
}
}
运行结果:
上面的代码。笔者在BaseA类里面并没有实现call2的方法。显然现在的功能对我们写框架的人来写太棒了。在也不用担心增加一个接方法而去考虑有多少个人用这个接口了。
那么问题来了。我们在写代码的过程中,一定会遇到方法相同的情况吧。这个时候JAVA8提供了三个标准来确定用哪一个。
- 类或父类的方法优先级高于接口默认的方法。
- 如果上面不行的话,谁拥有最具体的实现的话,就用谁。
- 如果都不能确定的情况下,就必须显性的调用。来指定他要调哪一个。
举例子。A和B都是接口。其中B继承了A。同时C实现了A和B。这个时候调用C会是什么样子。
A:
public interface A { default void call() {
System.out.println("A call");
}
}
B:
public interface B extends A {
default void call() {
System.out.println("B call");
}
}
C:
public class C implements A, B { }
D:
public static void main(String[] args) {
// TODO Auto-generated method stub C c = new C(); c.call(); }
运行结果:
上面A和B都是接口。他们有call方法。其中关键是B继承了。说明B拥有A的一切方法。那么是不是说B就是最具体实现的。如果你们只用第一个标准的话,那是肯定不行的。
还是简单一点,我们把B继承A的这个关系去掉,在来看看。
不好意思好像报错了。所以只能苦一下了。显性调用。
package com.aomi; public class C implements B, A { public void call() {
B.super.call();
}
}
当然除了上面之外,你还是可以定义静态方法和常量。这个时候有人就会说他不是跟抽象类很像吗?是很像。可是不一样子。抽象类是不是可以实例一个字段。但是接口却不行。还有抽像类你只能单继承。接口就可以多继承了。
JAVA8给我带了什么——并行流和接口新功能的更多相关文章
- 【Java8新特性】关于并行流与串行流,你必须掌握这些!!
写在前面 提到Java8,我们不得不说的就是Lambda表达式和Stream API.而在Java8中,对于并行流和串行流同样做了大量的优化.对于并行流和串行流的知识,也是在面试过程中,经常被问到的知 ...
- java8学习之收集器枚举特性深度解析与并行流原理
首先先来找出上一次[http://www.cnblogs.com/webor2006/p/8353314.html]在最后举的那个并行流报错的问题,如下: 在来查找出上面异常的原因之前,当然得要一点点 ...
- Java8新特性 并行流与串行流 Fork Join
并行流就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流. Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作. Stream API 可以声明性地通过 para ...
- 在使用Java8并行流时的问题分析
最近在使用Java8的并行流时遇到了坑,线上排查问题时花了较多时间,分享出来与大家一起学习与自查 // 此处为坑 List<Java8Demo> copy = Lists.newArray ...
- java8新特性——并行流与顺序流
在我们开发过程中,我们都知道想要提高程序效率,我们可以启用多线程去并行处理,而java8中对数据处理也提供了它得并行方法,今天就来简单学习一下java8中得并行流与顺序流. 并行流就是把一个内容分成多 ...
- Java8并行流使用注意事项
对于从事Java开发的童鞋来说,相信对于Java8的并行流并不陌生,没错,我们常常用它来执行并行任务,但是由于并行流(parallel stream)采用的是享线程池,可能会对我们的性能造成严重影响, ...
- Java8新特性 - 并行流与串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性地通过parallel()和 ...
- java8学习之自定义收集器深度剖析与并行流陷阱
自定义收集器深度剖析: 在上次[http://www.cnblogs.com/webor2006/p/8342427.html]中咱们自定义了一个收集器,这对如何使用收集器Collector是极有帮助 ...
- 避坑 | Java8使用并行流(ParallelStream)注意事项
示例分析 /** * 避坑 | Java8使用并行流(ParallelStream)注意事项 * * @author WH.L * @date 2020/12/26 17:14 */ public c ...
随机推荐
- Centos7.2下OpenVPN 环境完整部署记录
关于OpenVPN的有关介绍及为何使用OpenVPN在此就不做赘述了,下面直接记录Centos7.2系统下部署OpenVPN环境的操作过程: 1) 先将本机的yum换成阿里云的yum源 [root@t ...
- centos7下部署iptables环境纪录(关闭默认的firewalle)
CentOS7默认的防火墙不是iptables,而是firewall.由于习惯了用iptables作为防火墙,所以在安装好centos7系统后,会将默认的firewall关闭,并另安装iptables ...
- 移动端触摸(touch)事件
移动端时代已经到来,作为前端开发的我们没有理由也不应该坐井观天,而是勇敢地跳出心里的那口井,去拥抱蔚蓝的天空.该来的总会来,我们要做的就是接受未知的挑战.正如你所看到的,这是一篇关于移动端触摸事件的文 ...
- Linux内核分析 笔记五 扒开系统调用的三层皮(下) ——by王玥
(一)给MenuOs增加time和time-asm命令 更新menu代码到最新版 在main函数中增加MenuConfig 增加对应的Ttime和TimeAsm函数 make rootfs (二)使用 ...
- Linux内核读书笔记第三周 调试
内核调试的难点在于它不能像用户态程序调试那样打断点,随时暂停查看各个变量的状态. 也不能像用户态程序那样崩溃后迅速的重启,恢复初始状态. 用户态程序和内核交互,用户态程序的各种状态,错误等可以由内核来 ...
- [2017BUAA软工]第一次个人项目 数独的生成与求解
零.Github链接 https://github.com/xxr5566833/sudo 一.PSP表格 PSP2.1 Personal Software Process Stages 预估耗时(分 ...
- IP工具类
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletReques ...
- WorkStation 虚拟机迁移到 ESXi的后续处理.
自己遇到了然后按照blog http://blog.sina.com.cn/s/blog_79a8b8e10102w8bm.html 解决 特此记录一下. 将Workstation的vmdk文件导入到 ...
- 排查mysql innodb Lock wait timeout exceeded; try restarting transaction的问题
OMG写的时候崩溃了一次. 触发关注这个问题的事情是 我们在使用pt-online-schedule 改表的时候总是拿不到锁,并且报出mysql innodb Lock wait timeout ex ...
- Linux基础学习(3)--初学注意
第三章——初学注意 一.学习Linux的注意事项 1.Linux严格区分大小写 2.Linux中所有内容以文件形式保存,包括硬件: (1)硬盘文件是/dev/sd[a-p] (2)光盘文件是/dev/ ...