java1.8的新特性 lambda表达式、链式编程、函数式接口、Stream流式计算
1,函数式接口
它有有个专门的注解,含有@FunctionalInterface注解的接口,就只有一个方法的接口, 我们平时使用的Runable 接口就是一个函数式的接口。函数式的接口可以使用lambda 表达式来简编程。
@FunctionalInterface
public interface Runnable {
void run();
}
常见的函数式接口都在java.util.function包下面下面我来重点学习几个
//四大函数式接口 只要是函数式接口 支持lambda表达式
public class FunctionalInterface {
public static void main(String[] args) {
//Function 函数式接口
//第一个为输入参数 第二个为输出参数
/*Function<Object, Object> function = new Function<Object, Object>(){
@Override
public Object apply(Object o) {
return o;
}
};*/
Function<Object,Object> function = (str)->{return str;};
System.out.println(function.apply("aaaaa"));
//Predicate 断定型函数式接口
//有一个输入参数,但是返回值只能是boolean值
/*Predicate<String> predicate = new Predicate<String>(){
//判断字符串是否为空
@Override
public boolean test(String s) {
return s.isEmpty();
}
};*/
Predicate<String> predicate = (str)->{return str.isEmpty();};
System.out.println(predicate.test("aaa"));
//Consumer 消费型函数式接口
//只有参数,没有返回值
/* Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};*/
Consumer<String> consumer = (str)->{ System.out.println(str);};
consumer.accept("没有返回值哟");
//Supplier 供给型函数式接口
//没有参数,只有返回值
/*Supplier supplier = new Supplier() {
@Override
public Object get() {
return "没有参数,只有返回值";
}
};*/
Supplier supplier = ()->{return "没有参数,只有返回值";};
System.out.println(supplier.get());
}
}
2,Stream流式计算与链式编程
java8的流式处理极大的简化了对于集合的操作,实际上不光是集合,包括数组、文件等,只要是可以转换成流,我们都可以借助流式处理。java8通过内部迭代来实现对流的处理,一个流式处理可以分为三个部分:转换成流、中间操作、终端操作。如下图:
学习stream其实做一个练习题就可以了,现在有一个需求如下
/**
* Stream流式计算
* 本题目要求:一分钟内完成此题,只能用一行代码实现!*现在有5个用户!、、
* 筛选:
* 1、ID 必须是偶数*
* 2,年龄必额大于23岁*
* 3,用户名转为大写字母卡
* 4,用户名字母倒着排序、
* 5,只输出一个用户!
* 还可以把结果拼接成一个字符串还可以把结果转成集合
* Collectors.toList()
* Collectors.joining(";")
*/
public class Stream {
public static void main(String[] args) {
User u1 = new User(1, "a", 21);
User u2 = new User(2, "b", 22);
User u3 = new User(3, "c", 23);
User u4 = new User(4, "d", 24);
User u5 = new User(6, "e", 25);
List<User> users = Arrays.asList(u1, u2, u3, u4, u5);
users.stream()
.filter(u -> {
return u.getId() % 2 == 0;
})
.filter(u -> {
return u.getAge() > 23;
})
.map((u) -> {
return u.getName().toUpperCase();
})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}
用流的好处就是速度会很快。为什么说他快,可以看一个数据就知道,计算1到一亿的和,我们用三种方式计算for循环jdk1.7的Forkjoin还有就是jdk1.8的stream流
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{
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();
}
}
//for循环计算
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (int i = 1; i <=10_0000_0000; i++) {
sum+=i;
}
long end = System.currentTimeMillis();
System.out.println("for--->:sum="+sum+"时间:----》"+(end-start) );
}
//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("forkjoin--->sum="+sum+"时间:----》"+(end-start) );
}
//Stream计算
public static void test3(){
long start = System.currentTimeMillis();
//rangeClosed()以1的增量步长从startInclusive到endExclusive返回一个有序的LongStream。这包括初始元素和最后一个元素。
//reduce()合并流的元素并产生单个值。
long sum = LongStream.rangeClosed(1,10_0000_0000L).reduce(0,Long::sum);
long end = System.currentTimeMillis();
System.out.println("stream--->sum="+sum+"时间:----》"+(end-start) );
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1();//12224
test2();//10038
test3();//438
}
}
这里简单说下,ForkJoin 并行执行任务,也是在并发包下,经常在大数据计算量使用,把一个大任务拆分成若干个小任务去执行,最后把结果汇总,如果不是超级大的数据量,使用ForkJoin没必要
特点是工作窃取:当执行一个任务时它可以将其拆分成更小的任务执行,并将小任务加到线程队列中,当一个线程执行完自己的任务后,他会再从一个随机从没有完成线程的队列中偷一个并把它放在自己的队列中来完成提高工作效率,维护是靠一个双端队列,
可以明显看到执行结果,stream流的速度比for循环还是ForkJoin快了不知道多少倍,从性能考虑还是stream流快的多