Lambda表达式与StreamAPI
Lambda表达式与StreamAPI
这两个的目的都是想要使得Java能够实现函数式编程
Lambada表达式主要针对接口,函数式接口进行的优化,简化代码
StreamAPI主要是针对集合的处理操作进行的优化,简化代码
Lambda表达式
是一个匿名的函数,为了把方法体的实现代码当做数据一样进行传递
JDK1.8之前,用匿名内部类完成
//目的就是传递run()方法的实现代码 Runnable r = new Runnable(){ public void run(){ ...... } }Runnable r = new Runnable(){ public void run(){ ...... } } new Thread(new Runnable(){ public void run(){ ...... } }).start();
使用Lambda表达式
Runnable r = () -> {....};
new Thread(() -> {....}).start();
语法
格式: (形参列表) -> {Lambda体}
四种形式
(1)无参无返回值
格式:() -> {Lambda体}
说明:
(1)无参,()
(2)如果Lambda体是多句语句,那么{}是不可以省略
(3)如果Lambda体是1句语句,那么{}如果不省略,那么语句后面要加; ,即{;}
(4)如果Lambda体是1句语句,那么{;}可以省略
代表
Runnable r = () -> {System.out.println("hello");};
Runnable r = () -> System.out.println("hello");
(2)有参无返回值
格式:(形参列表) -> {Lambda体}
说明:
(1)如果Lambda体是多句语句,那么{}是不可以省略
(2)如果Lambda体是1句语句,那么{}如果不省略,那么语句后面要加; ,即{;}
(3)如果Lambda体是1句语句,那么{;}可以省略
(4)如果参数列表的参数类型是可以确定的,那么类型可以省略,如果形参个数是多个,那么()不可以省略
(5)如果参数列表的个数只有1个,类型可以确定,()可以省略,形参名不可以省略,但是形参名可以和接口的抽象方法的形参名不一样
代表
消费型
Consumer<T> void accept(T t)
java.lang.Iterable<T>
forEach(Consumer<T>)
集合对象.forEach(t -> System.out.println(t));
(3)无参有返回值
格式:() -> {Lambda体} Lambda体中,肯定有return语句
说明:
(1)无参,()不可以省略
(2)如果Lambda体是多句语句,那么{}是不可以省略,,每一个语句后面要;,并且必须有return 返回值;的语句
(3)如果Lambda体是1句语句,那么{}如果不省略,那么一定是{return 返回值;}的语句
(4)如果Lambda体是1句语句,那么{;}可以省略,而且return要省略
代表
供给型
Supplier<T> T get()
java.util.Optional<T> orElseGet(Supplier)
Optional<String> opt = Optional.ofNullable(address);
String address = opt.orElseGet(() -> new String("北京"));
(4)有参有返回值
格式:(形参列表) -> {Lambda体}
说明:
(1)如果Lambda体是多句语句,那么{}是不可以省略,并且必须有return 返回值;的语句
(2)如果Lambda体是1句语句,那么{}如果不省略,那么一定是{return 返回值;}的语句
(3)如果Lambda体是1句语句,那么{;}可以省略,而且return也可以省略
(4)参数列表,如果类型可以确定,那么类型可以省略,()不可以省略
(5)参数列表,如果类型可以确定,并且参数个数只有一个,那么类型和()都可以省略
代表
java.util.Comparator<T>: int compare(T t1, T t2)
Comparator<Student> com = (t1,t2) -> t1.getId() - t2.getId();
Predicate<T> : boolean test(T t)
Predicate<Employee> p = t -> t.getSalary() > 10000;
Function<T,R> : R apply(T t)
如果Employee对象的薪资低于10000,涨薪10%,并且返回新工作
Function<Employee, Double> f = e -> { if(e.getSalary()<10000){ e.setSalary(e.getSalary()*1.1; } return e.getSalary(); }
使用的形式
1、给函数式接口的变量赋值:多态引用 Comparator<Student> com = (t1,t2) -> t1.getId() - t2.getId();
2、给函数式接口的形参赋值:多态参数 new TreeSet( (t1,t2) -> t1.getId() - t2.getId());
函数式接口
函数式接口也是接口,是个特殊的接口,SAM型接口 SAM:Single Abstract Method 使用注解声明@FunctionInterface
在这个类接口中只有一个抽象方法,但是可以有静态方法和默认方法以及Object中的方法
只有函数式接口的变量或形参才能用Lambda表达式赋值
四个核心的函数式
1、消费型
Consumer<T> void accept(T t)
延伸版:BiConsumer<T,U>: void accept(T t,U u)
2、供给型
Supplier<T> T get()
DoubleSupplier Double get()
3、功能型
Function<T,R> R apply(T t)
延伸版
BiFunction<T,U,R> R apply(T t,U u)
BinaryOperator<T> T apply(T t2, T t2)
4、断定型
Predicate<T> : boolean test(T t)
延伸版:BiPredicate<T,U> boolean test(T t ,U u)
再简化版
方法引用
条件
(1)Lambda体通过调用一个现成的方法来完成
(2)Lambda的形参列表刚好是用于这个方法的实参
形式有三种
(1)实例对象::实例方法名
集合对象.forEach(t -> System.out.println(t));
集合对象.forEach(System.out::println)
(2)类名::静态方法名
Supplier<Double> s = () -> Math.random(); Supplier<Double> s = Math::random;
BiFunction<Integer,Integer,Integer> b = (a,b) -> Math.max(a,b); BiFunction<Integer,Integer,Integer> b = Math::max;
(3)类名::实例方法名
Comparator<String> c = (t1,t2) -> t1.compareTo(t2);
Lambda表达式的形参的第一个作为调用方法的对象,第二个,作为这个实例方法的实参
Comparator<String> c = String::compareTo;
构造器引用
条件
(1)Lambda体通过调用一个构造器完成,返回这个新建的对象
(2)Lambda的形参列表刚好是用于这个构造器的实参
示例
Fuction<String ,Employee> f = name -> new Employee(name);
Fuction<String ,Employee> f = Employee::new;
数组构造的引用
条件
(1)Lambda体通过创建一个数组对象完成,返回这个新建的数组对象
(2)Lambda的形参列表刚好是用于这个数组对象的创建的长度
示例
Fuction<Integer ,Employee[]> f = len -> new Employee[len];
Fuction<Integer ,Employee[]> f = Employee[]::new;
StreamAPI
作用
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
特点
1、Stream不负责存储数据
2、Stream的操作不影响数据源,每次操作返回一个新的Stream
3、Stream的中间操作是一个“懒惰求值”,一直要到终结操作,才会一口气完成
步骤
1、创建Stream
四种方式
1、Collection集合对象.stream()
ArrayList<String> list = new ArrayList<>();
//创建stream
Stream<String> stream = list.stream();
2、Arrays.stream(数组对象)
String[] arr = {"hello","world","java","lambda","stream"};
Stream<String> stream = Arrays.stream(arr);
3、Stream.of(T...)
Stream<String> stream = Stream.of("hello","world","java","lambda","stream");
4、无限流
Stream.iterator(..)
//示例 @Test public void test4(){ //Interface UnaryOperator<T>是特殊的Function<T,T>: T apply(T t) Stream<Integer> stream = Stream.iterate(1, x -> x+2); //中间操作 stream = stream.limit(10);//取前面的10个 stream.forEach(System.out::println); }
Stream.generate(xx)
//示例 @Test public void test5(){ //Supplier<T> :T apply() // Stream<Double> generate = Stream.generate(() -> Math.random()); Stream<Double> stream = Stream.generate(Math::random); // stream = stream.limit(5); // stream.forEach(System.out::println); stream.limit(5).forEach(System.out::println); }
2、中间操作
(1)筛选与切片
A:过滤:filter
B:去重:distinct
C:取前面的几个:limit
D:跳过前面几个:skip
* 要取中间几个:skip + limit
(2)映射
A:map(Function):对Stream中的每一个元素执行某个操作,返回值组成一个新的Stream
B:返回值是特殊类型
* mapToInt()
* mapToLong()
* mapToDouble()
C:flatMap(Function):对stream的每一个元素执行某个操作,返回值是一个stream,这些个stream再合成一个大的stream
(3)排序
A:sorted():自然排序
B:sorted(Comparator):定制排序
3、终结操作
(1)遍历
forEach(Consumer)
(2)匹配与查找
A:allMatch(Predicate p):每一个元素是否都满足某个条件
B:anyMatch(Predicate p):是否有一个元素满足条件
C:noneMatch(Predicate p):是否所有元素都不满足
*
D:findFirst():返回第一个
E:findAny():如果是并行流,返回的结果是任意一个,如果是一个稳定的流,返回的结果是和findFirst()一样
*
F:count():统计
G:max(Comparator)
H:min(Comparator)
(3)规约
A:Optional<T> reduce(BinaryOperator<T> accumulator)
BinaryOperator<T>是一个BiFunction<T,T,T>接口: T apply(T t1, T t2)
B:T reduce(T identity,BinaryOperator<T> accumulator)
(4)收集
<R,A> R collect(Collector<? super T,A,R> collector)
Collector 接口中方法的实现决定了如何对流执行收集的操作
Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,例如、List,Set,Map