java8中Lambda表达式和Stream API
一、Lambda表达式
1.语法格式
Lambda是匿名函数,可以传递代码。使用“->”操作符,改操作符将lambda分成两部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能,也就是实现接口方法的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
// 语法格式一:无参、无返回值 @Test public void test1() { Runnable runable = () -> System.out.println("hello lambda!"); runable.run(); } // 语法格式二:有一个参、无返回值 @Test public void test2() { Consumer< String > consumer = (args) -> System.out.println("hello!" + args); consumer.accept("lambda"); } // 语法格式三:有多个参、有返回值,并且有多条执行语句,用大括号包围 @Test public void test3() { Comparator< Integer > com = (x, y) -> { System.out.println("hello lambda!"); return Integer.compare(x, y); }; int rs = com.compare(2, 2); System.out.println(rs); } // 语法格式四:右侧如果只有一条执行语句,可以省略大括号和return @Test public void test4() { Comparator< Integer > com = (Integer x, Integer y) -> Integer.compare(x, y); int rs = com.compare(2, 2); System.out.println(rs); } |
注:lambd可以省略接口参数类型,java编译器会根据上下文进行类型推断
2.函数式接口
(1)只包含一个抽象方法的接口,称为函数式接口,可以在任意函数式接口上使用 @FunctionalInterface 注解,lambda需要函数式接口的支持。
(2)java8内置四大核心函数式接口:
Consumer<T>消费型接口:void accept(T t)
1
2
3
4
5
6
|
@Test public void test2() { Consumer< String > consumer = (args) -> System.out.println("hello!" + args); consumer.accept("lambda"); } |
Supplier<T>供给型接口:T get()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Test public void test5() { List< Integer > rs=getNumList(6,() -> (int)(Math.random()*100)); System.out.println(rs); } public List< Integer > getNumList(int size,Supplier< Integer > sup){ List< Integer > list=new ArrayList< Integer >(); for (int i = 0; i < size; i++) { Integer e=sup.get(); list.add(e); } return list; } |
Function<T, R>函数型接口:R apply(T t)
1
2
3
4
5
6
|
@Test public void test6() { Function< String , Integer> fun=(str)->str.length(); int len=fun.apply("lambda"); System.out.println(len); } |
Predicate<T>断定型接口:boolean test(T t)
1
2
3
4
5
6
|
@Test public void test7() { Predicate< String > check=(str)->str.equals("lambda"); boolean rs=check.test("lambda"); System.out.println(rs); } |
3.方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!( lambda要实现抽象方法的参数列表,必须与方法引用的方法参数列表保持一致! )。
方法引用:使用操作符 “ ::” 将对象或类和方法的名字分隔开来。
三种主要使用情况:
对象::方法名
1
2
3
|
PrintStream ps=System.out; Consumer< String > consumer = ps::println; consumer.accept("lambda"); |
类::静态方法名
1
2
3
|
Comparator< Integer > com = Integer::compare; int rs = com.compare(2, 2); System.out.println(rs); |
类::实例方法名(前提条件:lambda参数列表的第一个参数是实例方法的调用者,第二个参数是实例方法的入参)
1
2
|
BiPredicate< String ,String> check=(str1,str2)->str1.equals(str2); BiPredicate< String ,String> check1=String::equals; |
4.构造器引用
与函数式接口相结合,自动与函数式接口中方法兼容(需要调用的构造器方法参数列表要与函数式接口中方法的参数列表一致)
格式: ClassName::new
5.数组引用
格式: type[] :: new
二、Stream API
1.说明解释
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。流(Stream)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算! ”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
2.Stream 操作步骤
(1)创建Stream
①可以通过Collection系列集合提供的stream()或者parallelStream()获得
1
2
|
List< String > list=new ArrayList< String >(); Stream< String > stream=list.stream(); |
②可以通过Arrays的静态方法stream()获得数组流
1
2
|
Person[] ps=new Person[10]; Stream< Person > stream=Arrays.stream(ps); |
③可以通过Stream的静态of()
1
|
Stream< String > steam=Stream.of("aa","bb","cc"); |
④可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流
(2)中间条件操作
说明:多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
①筛选与切片
filter——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
List< Person > personList = Arrays.asList( new Person("李四", 20, 10), new Person("张三", 40,30), new Person("王五", 28, 15), new Person("赵六", 60, 60), new Person("赵六", 60, 60), new Person("赵六", 60, 60), new Person("田七", 8,2) ); Stream< Person > stream=personList.stream(); stream.filter((p)->p.getAge()>20)//过滤保留age>20 .limit(5)//只取前两个 .skip(1)//跳过前一个,返回剩下的 .distinct()//去重,自动定义对象去重要重写equals和hashcode .forEach(System.out::println); |
②映射
map——接收 Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
1
2
3
|
Stream< Person > stream=personList.stream(); stream.map((p)->p.getName())//提取name,组成新的Stream流 .forEach(System.out::println); |
③排序
sorted()——自然排序(Comparable)
sorted(Comparator com)——定制排序
(3)执行操作
①查找与匹配
allMatch——检查是否匹配所有元素,返回boolean
anyMatch——检查是否至少匹配一个元素,返回boolean
noneMatch——检查是否没有匹配的元素,返回boolean
findFirst——返回第一个元素
findAny——返回当前流中的任意一个元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值