一.为什么使用lambda表达式
1.它实际上是以匿名内部类为基础的。匿名内部类实际上就是在需要用到某个接口的实现的时候,又不想去实现这个接口,那么就是用匿名内部类来作为参数传递。lambda表达式对匿名内部类进行简化
2.现在我们来看一个例子,就知道lambd的好处了。
例子:需求1:现在有一个员工的集合,需要过滤出年级小于等于20岁的员工 需求2:过滤出工资小于5000的员工 需求3:过滤xxx 需求4.......,这样的需求在不同的地方需要用到
public class TestLambda { //由于有很多的类似的需求,所以提取优化 List<Employee> emps = Arrays.asList( new Employee(101, "张三", 18, 9999.99), new Employee(102, "李四", 59, 6666.66), new Employee(103, "王五", 28, 3333.33), new Employee(104, "赵六", 8, 7777.77), new Employee(105, "田七", 38, 5555.55) );
3.方案一使用匿名内部类
第一步先创建一个接口MyPredicate.java
里面一个方法 public boolean test(Employee t);
第二步,写过滤方法,两个参数,1)要过滤的员工集合 2)接口MyPredicate
public void filterEmployee(List<Employee> list,MyPredicate<Employee> pre){ List<Employee> listresult =new ArrayList<>(); for (Employee employee : list) { if(pre.test(employee)){ listresult.add(employee); System.out.println(employee); } } }
第三步,根据实际需求使用匿名内部类调用
(现在使用匿名内部类,这样做的好处是不用每次都去实现接口MyPredicate,创建很多的实现类,
//只需要在调用的时候写test方法的具体实现就好)
@Test public void test01(){ System.out.println("haha1"); filterEmployee(emps,new MyPredicate<Employee>() { @Override public boolean test(Employee t) { return t.getAge() <= 20; } }); }
4.方案2 -使用lamdba表达式
第一步,第二步同上
第三步,使用lamdba表达式 --- 相当于一个匿名内部类,更加简化
@Test public void test02(){ System.out.println("haha2"); filterEmployee(emps,(e) -> e.getAge() <= 20); }
5.方案三 stream + lambda表达式
前面的一二三步都不要了,直接使用下面代码
@Test public void test03(){ System.out.println("haha3"); Stream<Employee> filter = emps.stream().filter((e) -> e.getAge() >= 20); emps.stream().filter((e) -> e.getAge() >= 20).forEach(System.out::println); System.out.println(); } }
到了这里,我们看到了匿名内部类的好处,对于代码的简化有很大的提高
二.lambda表达式的基础语法
1.Lambda 表达式的基础语法 ->
Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分:
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
2.语法格式一:无参数,无返回值
() -> System.out.println("Hello Lambda!");
3.语法格式二:有一个参数,并且无返回值
(x) -> System.out.println(x)
4.语法格式三:若只有一个参数,小括号可以省略不写
x -> System.out.println(x)
5.语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
Comparator<Integer> com = (x, y) -> { System.out.println("函数式接口"); return Integer.compare(x, y); };
6.语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
7.语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);//上面代码就一直都是省略的
三.Lambda需要函数式接口的支持
1.什么是函数式接口
一个接口中只有一个抽象方法,那么这个接口就叫做函数式接口
接口上面加@FunctionalInterface,那么这个接口必须是函数式接口,会自动检查语法
@FunctionalInterface public interface MyFunction { public String getValue(String str); }
lambda表达式也就是写函数式接口的实现
2.Java8 内置的四大核心函数式接口
1)消费型接口,有参数,没有返回值
Consumer<T> 接口
void accept(T t) 接口方法
2)供给型接口,没有参数,有返回值
Supplier<T>
T get();
3)函数型接口,有参数,也有返回值
Function<T, R>
R apply(T t)
4)断言型接口,有参数,有返回值,返回值为boolean
Predicate<T>
boolean test(T t);
四.lambda表达式-方法引用
若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用
1. 对象的引用 :: 实例方法名
2. 类名 :: 静态方法名
//Employee::getAge Employee静态方法getAge
IntSummaryStatistics collect5 = emps.stream().collect(Collectors.summarizingInt(Employee::getAge));
3. 类名 :: 实例方法名
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
* 注意:
* ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
五.lambda表达式-构造器引用
构造器的参数列表,需要与函数式接口中参数列表保持一致!
格式-类名 :: new
//不使用构造器引用
emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(()-> new HashSet<>()));
//使用构造器引用
HashSet<String> hs = emps.stream() .map(Employee::getName) .collect(Collectors.toCollection(HashSet::new)); //toCollection里面要插入一个Collect的实例,表示要转换成Collect的哪个子类
六.lambda表达式-数组引用
和构造器引用一样,只不过构造的类型是数组
类型[] :: new;
七.lambda表达式-Stream
1.Stream数据源
集合、数组等
2.操作特性
- stream自己不会储存数据
- 对数据源进行操作会产生一个新的流,数据源不会被影响
- Stream的操作是延迟的。等到需要结果的时候才会执行
3.使用steram步骤
- 创建stream
- 中间操作,一系列的流水操作
- 中止操作
4.创建stream的4种方式
通过collection系列集合的stream()方法[串行流]或者parallelStream()[并行流]方法
里面是串行流,并行流类型
1)集合对象调用stream()方法
List<Integer> list = new ArrayList<>(); Stream<Integer> stream = list.stream();
2)Arrays.Stream()获取一个数组流
Integer[] arr = new Integer[3]; Stream<Integer> stream2 = Arrays.stream(arr);
3)Stream的静态方法of()
Stream<Integer> of = Stream.of(1,2,3);
4)创建无线流
A.迭代
参数1是起始值 参数2是一个函数性接口(lambda)
Stream<Integer> iterate = Stream.iterate(0, (x) -> {return x+2;}); iterate.limit(10).forEach(System.out::println); B.生成 Stream<Double> generate = Stream.generate(() -> Math.random()); generate.limit(10).forEach(System.out::println);
5.Stream中间操作-也就是对流的处理
1)常用中间过程
filter 过滤
limit 限定数量
skip(n) 跳过元素 不要前n个元素
distinct 通过新生成元素的hashCode()方法和equals()方法去重
2)特性1-惰性求值:中间操作若是没有终止操作是不会执行的
执行下面代码不会有任何反应
List<Employee> emps = Arrays.asList( new Employee(102, "李四", 59, 6666.66), new Employee(101, "张三", 18, 9999.99), new Employee(103, "王五", 28, 3333.33), new Employee(104, "赵六", 8, 7777.77), new Employee(104, "赵六", 8, 7777.77), new Employee(104, "赵六", 8, 7777.77), new Employee(105, "田七", 38, 5555.55) ); emps.stream().filter((x) -> {System.out.println("hahaha"); return true; }); //这里只有中间操作filter,后面没有终止操作
执行下面操作才会执行
List<Employee> emps = Arrays.asList( new Employee(102, "李四", 59, 6666.66), new Employee(101, "张三", 18, 9999.99), new Employee(103, "王五", 28, 3333.33), new Employee(104, "赵六", 8, 7777.77), new Employee(104, "赵六", 8, 7777.77), new Employee(104, "赵六", 8, 7777.77), new Employee(105, "田七", 38, 5555.55) );
emps.stream().filter((x) -> {System.out.println("hahaha");
return true;
}).forEach(System.out::println);
//这里filter之后,接了foreach这个终止操作
3)特性2-短路(里面还包含了两个中间过程filter和limit的实例)
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "张三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
emps.stream().filter((x) -> {System.out.println("hahaha"); return x.getAge() >10; }).limit(3).forEach(System.out::println); 打印结果: hahaha Employee [id=102, name=李四, age=59, salary=6666.66, status=null] hahaha Employee [id=101, name=张三, age=18, salary=9999.99, status=null] hahaha Employee [id=103, name=王五, age=28, salary=3333.33, status=null] 只打印了3个“哈哈哈”,当获取到了limit限制的3个后就不去遍历了。
也就是说,它在过滤的时候同时也在进行limit,过滤达到了3个,limit生效,不再进行遍历filter。
也不是遍历所有完成filter,再进行limit
6.中间操作-映射
map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
map实例
emps.stream().map((x)->{return x.getName();}).forEach(System.out::println); //map((x)->{return x.getName();}) 是返回一个name的流
map和flatmap比较
TestStreamAPI1::filterCharacter这个方法返回值类型是一个流Stream Stream<Stream<Character>> stream2 = strList.stream().map(TestStreamAPI1::filterCharacter); 若flatMap中的函数实现的返回值是流,则map返回的是流 Stream<Character> stream3 = strList.stream().flatMap(TestStreamAPI1::filterCharacter);
简单来说flatmap()括号里面返回值类型是流(此时若是map,形成的就是Stream<Stream<T>>),flatmap把这些流合并成一个流,形成的是Stream<T>。也就是说它可以实现同类流的合并。
类似 list.add()和List.addAll()
list.add()
List list = new ArrayList<>();
list.add(Arrays.asList(1,2,3));
System.out.println(list);
结果:[[1, 2, 3]] 里面是集合
List.addAll()
List list = new ArrayList<>();
list.addAll(Arrays.asList(1,2,3));
System.out.println(list);
结果:[1, 2, 3] 里面是int
7.中间操作-sorted
1) sorted()自然排序 按照对象的Comparable排序
List<Integer> asList = Arrays.asList(3,1,2); asList.stream().sorted().forEach(System.out::println); //结果:1 2 3
2)排序 sorted(Comparator)自定义排序
emps.stream().sorted((x,y) ->{ return x.getName().compareTo(y.getName()); }).forEach(System.out::println);
8.终止操作
1)查找和匹配
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
boolean allMatch = emps.stream().allMatch((x) -> x.getName().equals("李四")); System.out.println("allMatch-" + allMatch); boolean anymatch = emps.stream().anyMatch((x) ->x.getAge() == 18); System.out.println("allMatch-" + anymatch); boolean nonematch = emps.stream().noneMatch((x) ->x.getAge() == 18); System.out.println("allMatch-" + nonematch); Optional<Employee> findFirst = emps.stream().findFirst(); System.out.println(findFirst.get()); Optional<Employee> findAny = emps.parallelStream().findAny(); System.out.println(findAny.get()); long count = emps.stream().count(); System.out.println(count); Optional<Employee> max = emps.stream().max((x,y)-> Integer.compare(x.getAge(), y.getAge())); System.out.println(max.get().getAge()); Optional<Integer> min = emps.stream().map(Employee::getAge).min((x,y)-> Integer.compare(x,y)); System.out.println(min.get());
2)归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream() .reduce(0, (x, y) -> x + y); //两个参数 第一个参数是起始值 第二个参数是函数式接口-lambda表达式 System.out.println(sum); //55
3) collect
将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList()); //这里将流转换成一个新的集合