一、 Lambda表达式

1. Lambda表达式的好处

使用Lambda表达式可以使代码变得更加简介紧凑,并且Lambda表达式可以和StreamAPI等相结合,使代码更加简洁紧凑。Lambda表达式经常用来替代部分匿名内部类。

2. Lanbda表达式的语法

(parameters) -> expression或 (parameters) ->{ statements; }
参数:要重写的方法的形参列表
-> :lambda运算符
表达式/语句体:要重写的方法的方法体
Lambda 表达式是一种匿名函数(不是匿名内部类),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。它实质属于函数式编程的概念。

3. 注意事项

  1. 函数只有一条语句{ }是可以省略的
  2. 在定义参数的时候Lambda表达式的参数类型是可以省略的
  3. 如果只有一个参数这个时候()是可以省略的
  4. 如果定义返回值并且方法中只有一条执行语句的时候,这个时候return是可以省略的。

例如:F f=new-> num++;

4. 用法

引入内部类以及匿名内部类

  1. interface Fu{
  2. void add();
  3. }
  4. class Zi implements Fu{
  5. @Override
  6. public void add() {
  7. }
  8. }
  9. class TestA{
  10. //内部类
  11. class Zi implements Fu {
  12. @Override
  13. public void add() {
  14. }
  15. }
  16. public static void main(String[] args) {
  17. //匿名内部类
  18. Fu f=new Fu() {
  19. @Override
  20. public void add() {
  21. System.out.println("我是addddd方法");
  22. }
  23. };
  24. f.add();
  25. }
  26. }

引入Lambda

package com.bjsxt.demo03;

public class TestC {
    public static void main(String[] args) {
        System.out.println("------------1、没有参数也没有返回值--------------");
/*        F f=()-> System.out.println("我是add方法");
        f.add();*/
        System.out.println("------------2、有参数没有返回值--------------");
/*        F f=num -> System.out.println("我是方法add"+num);
        F f1=(int num)->{
            System.out.println("我是方法add"+num);
        };
        f.add(123);
        f1.add(234);*/
        System.out.println("------------3、有参数也有返回值--------------");
        F f=(int num)->{
            num++;
            return num; //直接使用return进行返回即可
        };
        F f1=num -> num++; //传入参数num 在方法中进行num++ 再把结果返回
        System.out.println(f.add(123));
        System.out.println(f1.add(234));
    }
}
interface F{
//    void add();
//    void add(int num);
    int add(int num);
}

二、 函数式接口

函数式接口 方法名 输入参数 输出参数 作用
消费型接口
Consumer
void accept(T t) T void 对类型为T的对象进行操作
供给型接口
Supplier
T get() void T 返回类型为T的对象
函数型接口
Function
R apply(T t) T R 对类型为T的对象进行操作,返回类型为R的对象
断言型接口
Predicate
boolean test(T t) T boolean 对类型为T的对象进行操作,返回布尔类型结果

三、 方法引用

有时候,Lambda体可能仅调用一个已存在方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰。方法引用是一个更加紧凑,易读的 Lambda 表达式,注意方法引用是一个 Lambda 表达式,方法引用操作符是双冒号 “::”。
方法引用有下面几种方式:
1) 对象引用::实例方法名
2) 类名::静态方法名
3) 类名::实例方法名
4) 类名::new(也称为构造方法引用)
5) 类型[]::new(也称为数组引用)
这里讲解了第一种方式(System.out::println、stu::getName)和第二种方式(Integer::compare
第一种A:

public class TestMethodRef1 {
    public static void main(String[] args) {
        //使用匿名内部类实现
        Consumer consumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer i) {
                System.out.println(i);
            }
        };
        consumer.accept(56);
        //使用lambda表达式实现
        Consumer<Integer> consumer1 = (i)->System.out.println(i);
        consumer1.accept(56);
        //使用方法引用
        //println()的参数类型、返回值类型正好和accept方法的参数类型、返回值类型相同
        Consumer<Integer> consumer2 = System.out::println;
        consumer2.accept(56);
    }
}

第一种B:

public class TestMethodRef2 {
    public static void main(String[] args) {
        Student stu = new Student(10,"zhangsan",23,100);
        //使用匿名内部类实现
        Supplier<String> supplier1= new Supplier<String>(){
            public String get() {
                return stu.getName();
            }
        };
        System.out.println(supplier1.get());
        //使用Lambda表达式实现
        Supplier<String> supplier2 = ()-> stu.getName();
        System.out.println(supplier2.get());
        //使用方法引用实现
        Supplier<String> supplier3 =  stu::getName;
        System.out.println(supplier3.get());
    }
}

第二种:

public class TestMethodRef3 {
    public static void main(String[] args) {
        //使用匿名内部类实现
        Comparator comparator1 = new Comparator<Integer>() {
            public int compare(Integer in1, Integer in2) {
                //return in1.intValue()-in2.intValue();
                return Integer.compare(in1,in2);
            }
        };
        System.out.println(comparator1.compare(12,34));
        //使用Lambda表达式实现
        Comparator<Integer> comparator2 = (in1,in2)->{ return Integer.compare(in1,in2);};
        System.out.println(comparator2.compare(12,34));
        //使用方法引用实现
        Comparator<Integer> comparator3 =Integer::compare;
        System.out.println(comparator3.compare(12,34));
    }
}

Stream作为Java8的一大亮点,它与java.io包里的InputStream和OutputStream是完全不同的概念。它是对容器对象功能的增强,它专注于对容器对象进行各种非常便利、高效的聚合操作或者大批量数据操作。
Stream API借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。同时,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。所以说,Java8中首次出现的 java.util.stream是一个函数式语言+多核时代综合影响的产物。
Stream有如下三个操作步骤:
一、创建Stream:从一个数据源,如集合、数组中获取流。
二、中间操作:一个操作的中间链,对数据源的数据进行操作。
三、终止操作:一个终止操作,执行中间操作链,并产生结果。

当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”。中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线。比如map (mapToInt, flatMap 等)、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered。
当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终止操作。终止操作将返回一个执行结果,这就是你想要的数据。比如:forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator。
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称作“惰性求值”。
流式编程1:中间操作和中止操作

public class TestStream1 {
public static void main(String[] args) {
List list = new ArrayList<>();
Collections.addAll(list,34,56,89,65,87,80,87,95,100,34,45);
//创建Stream
Stream stream = list.stream();
//进行中间操作
stream = stream.filter((x)->{if(x>=60) return true; return false;})//刷选掉不及格的
.distinct() //去重
.sorted((x1,x2)->{return -Integer.compare(x1,x2);})//降序排列
.limit(4)//只要前四个
.map((x)->x+5)//每个成绩加5分
.skip(2);//跳过前两个,从第三个开始
//进行终止操作:stream has already been operated upon or closed
//stream.forEach(System.out::println);//遍历
//System.out.println(stream.max((x1,x2)->x1-x2));;
//System.out.println(stream.count());
System.out.println(stream.findFirst());
}
}

流式编程2:并行流和串行流
什么是并行流?
简单来说,并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换 。

Fork/Join 框架
Fork/Join 框架: 就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总 。

使用ForkJoin和普通for实现1-1000000000000l求和效率对比

package com.bjsxt.demo;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
public class SumTask extends RecursiveTask {
private long start;
private long end;
private final int step = 2000000;//最小拆分成几个数相加
public SumTask(long start, long end) {
    this.start = start;
    this.end = end;
}
@Override //在这个方法中定义我们自己计算的规则
protected Long compute() {
    long sum = 0;
    if(end - start <= step ){
        //System.out.println("小于步长进行求和");
        //小于5个数,直接求和
        for (long i = start; i <=end; i++) {
            sum+=i;
        }
    }else{
        //大于5个数,分解任务
        long mid = (end + start)/2;
        SumTask leftTask = new SumTask(start,mid);
        SumTask rightTask = new SumTask(mid+1,end);
        //执行子任务
        leftTask.fork();
        rightTask.fork();
        //子任务,执行完,得到执行结果
        long leftSum = leftTask.join();
        long rightSum = rightTask.join();
        //System.out.println("join结果"+leftSum+"---"+rightSum);
        sum = leftSum+rightSum;
    }
    return sum;
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
    int  sum=0;
    long l = System.currentTimeMillis();
    for (int i = 0; i <=1000000000000l; i++) {
         sum+=i;
    }
    long l2 = System.currentTimeMillis();
    System.out.println("forx循环执行时间:"+(l2-l));

    //使用ForkJoin框架解决
    //创建一个ForkJoin池
    ForkJoinPool pool = new ForkJoinPool();
    //定义一个任务
    SumTask sumTask = new SumTask(1,1000000000000l);
    //将任务交给线程池
    long l3 = System.currentTimeMillis();
    Future<Long> future = pool.submit(sumTask);
    //得到结果并输出
    Long result = future.get();
    long l4 = System.currentTimeMillis();

    System.out.println("ForkJoin执行时间:"+(l4-l3));
}
    }

执行结果:

//创建Stream方式2:并行流,底层采用ForkJoin框架,结果并不按照集合原有顺序输出
System.out.println(“————————“);
Stream stream2 = list.parallelStream();//
stream2.forEach((x)->System.out.println(x+”—-“+Thread.currentThread().getName()));

流式编程3:创建Stream

public class TestStream2 {
public static void main(String[] args) {
//创建Stream方式1
List list = new ArrayList<>();
Collections.addAll(list,34,56,89,65,87,80,87,95,100,34,45);
Stream stream = list.stream();
stream.forEach(System.out::println);
//创建Stream方式2:并行流,底层采用ForkJoin框架,结果并不按照集合原有顺序输出
System.out.println("----------------");
Stream stream2 = list.parallelStream();//
stream2.forEach((x)->System.out.println(x+"---"+Thread.currentThread().getName()));
//创建Stream方式3:of()
System.out.println("----------------");
Stream stream3 = Stream.of(34,56,89,65,87,80,87,95,100,34,45);
stream3.forEach(System.out::println);
//创建Stream方式4
int [] arr = {34,56,89,65,87,80,87,95,100,34,45};
IntStream stream4 = Arrays.stream(arr);
stream4.forEach(System.out::println);
//创建Stream方式5
Stream stream5 = Stream.generate(()->Math.random());
// stream5.forEach(System.out::println);
//创建Stream方式6
Stream stream6 = Stream.iterate(6,(i)->2+i);
stream6.limit(5).forEach(System.out::println);
}
}