Lambda表达式

一、概述

1.1一句话概括

Lambda表达式是抽象方法的一个实现,是一种新的语法格式

1.2.本质

  • 回想一下,策略模式是为了解决运用不同的类调用不同的方法来处理不同情况下问题。幼儿园化一点,双十一的打折方案用双十一的类,双十二用双十二的类,但是其实就是打折的权重百分比不同而已,但是处理的还是商品和订单。
  • 但是不同的折扣就不同类,需要创建大量的类,所以我们后面就发明了匿名内部类,但是这些类就一个打折的那个方法不一样而已。下面是比较器匿名内部类。

    Comparator<Integer\> com = new Comparator<Integer\>() {  
      @Override  
      public int compare(Integer o1, Integer o2) {  
      return 1;  
        }  
    };
  • 再后来我们就用(Integer a,Integer b)->{return a-b;}这样的语法来表达这个方法。更加具体点就是下面的样子。

    Comparator<Integer> com=(a,b)->a-b;
  • 总结一下:
    1.用不同类处理相同对象,得到不同结果这样的情景在开发中大量存在。
    2.我们就用策略模式->匿名内部类->Lambda表达式来很好的处理这个问题。
    3.Lambda看起来像是函数式接口,像是传递一个函数一样。
    4.但是其实是匿名内部类的语法糖,编译器通过上下文推断获得要实现的那个内部类或接口的抽象方法,通过反射获取参数类型和返回值,然后把表达式转换为抽象方法的实现,进而创建具体的类,返回具体类的引用,用来赋值或者传参数。
    5.遇到匿名内部类应该首先去找对应的接口,找到实现的抽象方法。

二、函数式接口

2.1.为什么要有函数式接口?

运用Lambda表达式可以随时随地的创建对象,但是还是需要具体的接口的,不然无法知道要创建什么对象,还有Lambda表达式的内容已经接近裸奔。所以为了偷更多的懒,干脆内置常用接口,再通过泛型来增加参数类型的通用。

2.2.常用的函数型接口

Supplier Consumer Function Predicate
供给型 消费型 函数型 检测型
@FunctionalInterface  
public interface Consumer<T> {
    void accept(T t);
}
@FunctionalInterface  
public interface Function<T, R> {
    R apply(T t);
}
@FunctionalInterface  
public interface Predicate<T> {
    boolean test(T t);
}
@FunctionalInterface  
public interface Supplier<T> {  
    T get();  
}
@FunctionalInterface  
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}

三、方法引用与构造器引用

3.1方法引用

3.1.1一句话概括

就是用已实现的方法替代Lambda表达式,只要方法参数列表和返回值类型一致。

举个栗子:

集合类大部分实现了foreach方法
public void forEach(Consumer<? super E> action) 
ArrayList<Integer> integers = new ArrayList<>();  
integers.add(100);  
integers.add(101);  
integers.add(102);  
integers.forEach(System.out::println);

解释:
println有一个重载方法public void println(int x),替换了Consumer的void accept(T c),这里是双冒号都是语法格式。

3.2构造器引用

//0个参数  
Supplier<String> supplier = () -> new String();  
String s = supplier.get();  
Supplier<String> stringSupplier = String::new;  
stringSupplier.get();  

//1个参数  
Function<Integer, Integer> integerIntegerFunction = (x) -> new Integer(x);  
integerIntegerFunction.apply(1);  
Function<Integer, Integer> supplier1 = Integer::new;  
Integer apply = supplier1.apply(10);  
  
//2个参数  
BiFunction<String, Integer, Student> biFunction = Student::new;  
Student abc = biFunction.apply("abc", 10);  
System.out.println(abc);

Stream API

一、概述

1.1一句话概括

能够更加贴合业务需求的处理容器、数组的API,类似于SQL语句一样处理数据。

二、使用步骤

1.创建Stream

1.1.Collection系列集合通过stream()获取串行流或者parallelStream()获取并行流  
Collection<String> list = new ArrayList<String>();  
Stream<String> stringStream = list.stream();  

1.2.数组通过Arrays.stream()  
Student[] students = new Student[1];  
Stream<Student> ArrayStream = Arrays.stream(students);

1.3.Stream.of()方法  
Stream<Student> stream2 = Stream.of(students);  
Stream<String> stream3 = Stream.of("aa", "爸爸", "阿赐");

1.4.Stream.iterate()返回无限迭代流
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2); 
Stream<Integer> limit = stream4.limit(10);  
limit.forEach(System.out::println);

2.中间操作

2.0一句话概括

用Stream容器封装了数据,然后通过7个类似SQL的方法来处理Stream容器内的数据。

2.1筛选与切片
2.1.1filter(()->{})--接受Lambda表达式,从流中排除某些元素
2.1.2limit(n)--截断流,使其元素不超过给定数量
Stream<T> filter(Predicate<? super T> predicate);
students.stream().filter((x) -> {  
  System.out.println("执行到满足截断流就短路");  
    return x.getAge() > 35;  
}).limit(1).forEach(System.out::println);
2.1.3 skip(n)--跳过元素,返回一个扔掉前n个元素的流.若流中元素不足n个,则返回空流.
2.1.4 distinct--筛选,通过流生成元素的hahsCode()和equals()去除重复元素
//用stream API去重  
students.stream().filter((e) -> e.getAge() > 25).skip(1).distinct().forEach(System.out::println);

2.2映射与排序

2.2.1flatMap方法
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
2.2.2map方法
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

来自Stream.interface中,都是Function接口,前者返回Stream<? extends R>,后者返回R。

List<String> characters = Arrays.asList("aaaa", "bbbbb", "ccc", "ddd", "eee", "fff");  
System.out.println("flatMap 扁平化返回值");  
//扁平化,但是flatmap的参数只能够是Stream,flatMap方法的作用就是把返回的多个Stream连接成一个Stream
characters.stream().flatMap((s -> filterCharacter(s))).forEach(System.out::println);   
System.out.println("map 能把一个值映射成另外一个值");  
//Map方法是用返回值取代 原本Stream<T> T的值
characters.stream().map(s-> s.length()).forEach(System.out::println);
2.2.3sort把内部对象排序
//无参,默认数字升序,字典序  
List<String\> characters = Arrays.asList("bbba", "aaaa", "cccddddd", "cddf", "eee", "fff");  
characters.stream().sorted().forEach(System.out::println);  
System.out.println("-----------------------------------------");  
//直接使用字典序,不过是反向字典序  
characters.stream().sorted(Collections.reverseOrder(String::compareTo)).forEach(System.out::println);  
System.out.println("-----------------------------------------");  
  
 // 自定义比较器,先按长度,再按字典序 
 Comparator<String> stringComparator = (a, b) -> {  
  if (a.length() != b.length()) {  
  return b.length() - a.length();  
    } else {  
  return a.compareTo(b);  
    }  
};  
  
characters.stream().sorted(stringComparator).forEach(System.out::println);

3.终止操作

3.1查找与匹配

3.1.1匹配

allMatch、anyMatch、noneMatch方法判断是否存在某些数据
当然也可以用fliter方法

List<Student\> students = new ArrayList<Student\>();  
students.add(new Student("f", 28, Status.BUSY));  
students.add(new Student("a", 28, Status.FREE));  
students.add(new Student("abc", 28, Status.FREE));  
students.add(new Student("aac", 28, Status.FREE));  
  
students.add(new Student("acd", 28, Status.FREE));  
students.add(new Student("d", 28, Status.BUSY));  
students.add(new Student("e", 58, Status.BUSY));  
students.add(new Student("b", 28, Status.VOCATION));  
  
students.add(new Student("c", 28, Status.VOCATION));  
students.add(new Student("ab", 28, Status.FREE));  
students.add(new Student("aa", 28, Status.FREE));  
students.add(new Student("ac", 28, Status.FREE));  
  
//所有都匹配得上  
boolean b = students.stream().allMatch(e -> e.getStatus().equals(Status.BUSY));  
//一个匹配的上  
boolean b1 = students.stream().anyMatch(e -> e.getStatus().equals(Status.BUSY));  
  
System.out.println(b);  
System.out.println(b1);  
  
//一个都匹配不上?  
boolean b2 = students.stream().noneMatch(e -> e.getStatus().equals(Status.BUSY));  
System.out.println(b2);
3.1.2最大最小值
//返回流中定制排序的最大最小值  
Optional<Student> max = students.stream().max((a1, a2) -> a1.getAge() - a2.getAge());  
System.out.println(max.get());  
Optional<Student> min = students.stream().min((a1, a2) -> a1.getAge() - a2.getAge());  
System.out.println(min.get().getAge());
3.1.3返回元素个数
System.out.println("返回流个数");  
System.out.println("Serial Stream :"+students.stream().count());  
System.out.println("parallelStream is :"+students.parallelStream().count());

3.2归约与收集

3.2.1归约reduce方法做累加
Integer reduce = students.stream().map(Student::getAge).reduce(0, (x, y) -> x + y);  
System.out.println(reduce);  
  
Optional<Integer> reduce1 = students.stream().map(Student::getAge).reduce(Integer::sum);  
System.out.println(reduce1.get());
3.2.2把Stream数据回归到Collection数据源,Collectors工具类

1.toSet()转集合、toList()转链表

//collect 将流转换成为其他形式
//收集
List<Integer> list = students.stream().map(Student::getAge).collect(Collectors.toList());  
System.out.println(list);  
System.out.println("-------------------------------------");  
Set<Integer> collect = students.stream().map(Student::getAge).collect(Collectors.toSet());  
System.out.println(collect);

2.求和求平均值
averagingDouble()、summingDouble()

Double collect1 = students.stream().collect(Collectors.averagingDouble(Student::getAge));  
System.out.println(collect1);  
  
Double collect2 = students.stream().collect(Collectors.summingDouble(Student::getAge));  
System.out.println(collect2);

3.最大最小值
maxBy()、minBy()

//最大值  
Optional<Student> collect3 = students.stream().collect(Collectors.maxBy((s1, s2) -> Integer.compare(s1.getAge(), s2.getAge())));  
System.out.println(collect3.get());  
  
//最小值  
Optional<Student> collect4 = students.stream().collect(Collectors.minBy((s1, s2) -> Integer.compare(s1.getAge(), s2.getAge())));  
System.out.println(collect4.get());

4、求个数
counting()

Long collect5 = students.stream().collect(Collectors.counting());  
System.out.println(collect5);

5、分组groupingBy()

//分组  
Map<Status,List<Student>> map=students.stream().collect(Collectors.groupingBy(Student::getStatus));  
map.forEach((x,y)->System.out.println(x+" : "+y));  
System.out.println("多级分组");  
//多级分组,先按状态再按年龄
Map<Status, Map<Integer, List<Student>>> collect = students.stream().collect(Collectors.groupingBy(Student::getStatus, Collectors.groupingBy(Student::getAge)));  
System.out.println(collect);

运行结果:

按照分组
VOCATION : [Student(name=b, age=28, status=VOCATION), Student(name=c, age=28, status=VOCATION)]
FREE : [Student(name=a, age=28, status=FREE), Student(name=abc, age=28, status=FREE), Student(name=aac, age=28, status=FREE), Student(name=acd, age=28, status=FREE), Student(name=ab, age=28, status=FREE), Student(name=aa, age=28, status=FREE), Student(name=ac, age=28, status=FREE)]
BUSY : [Student(name=f, age=28, status=BUSY), Student(name=d, age=28, status=BUSY), Student(name=e, age=58, status=BUSY)]
多级分组
Busy明显分了Status和Age组
{VOCATION={28=[Student(name=b, age=28, status=VOCATION), Student(name=c, age=28, status=VOCATION)]}, 
FREE={28=[Student(name=a, age=28, status=FREE), Student(name=abc, age=28, status=FREE), Student(name=aac, age=28, status=FREE), Student(name=acd, age=28, status=FREE), Student(name=ab, age=28, status=FREE), Student(name=aa, age=28, status=FREE), Student(name=ac, age=28, status=FREE)]}, 
BUSY={58=[Student(name=e, age=58, status=BUSY)], 28=[Student(name=f, age=28, status=BUSY), Student(name=d, age=28, status=BUSY)]}}

6.分片partitioningBy()

Map<Boolean, List<Student>> collect1 = students.stream().collect(Collectors.partitioningBy((x) -> x.getAge() > 36));  
System.out.println(collect1);

运行结果:

{false=[Student(name=f, age=28, status=BUSY), Student(name=a, age=28, status=FREE), Student(name=abc, age=28, status=FREE), Student(name=aac, age=28, status=FREE), Student(name=acd, age=28, status=FREE), Student(name=d, age=28, status=BUSY), Student(name=b, age=28, status=VOCATION), Student(name=c, age=28, status=VOCATION), Student(name=ab, age=28, status=FREE), Student(name=aa, age=28, status=FREE), Student(name=ac, age=28, status=FREE)],
true=[Student(name=e, age=58, status=BUSY)]}

三、作用

3.1为什么需要Stream API?

Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)
Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

参考
[1] Java 8 中文 高清版(需要请私信)
[2] https://www.ibm.com/developer...
[3] https://www.bilibili.com/vide...


Winson
6 声望1 粉丝

把原本的个人笔记迁移到这里