Lambda、stream
Lambda
1.Lambda表达式概念
Lambda表达式是一个匿名函数,可理解为是一段可以传递的代码(即将函数像数据一样传递)。
Lambda表达式需要函数式接口(即只有一个抽象方法的接口)的支持。
Lambda操作符:->,该箭头一样的操作符将Lambda表达式拆分成两侧,
其中左侧写Lambda表达式的参数列表(接口中抽象方法的参数);右侧写Lambda表达式所需执行的功能(抽象方法的实现),即Lambda体。
2.Lambda表达式的基本语法
语法:
(parameters) -> expression
或
(parameters) ->{ statements; }
3.基础写法
我们先看几个简单的Lambda表达式的例子:
// 1. 不需要参数,返回值为 5 () -> 5 // 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2 * x // 3. 接受2个参数(数字),并返回他们的差值 (x, y) ->x –y // 4. 接收2个Integer型整数,返回他们的和 (Integer x, Integer y) ->x + y // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) ->System.out.print(s) |
通过上述几个简单例子我们总结一下Lambda表达式的基本写法:
1).左侧类型可以不写;
2).左侧只有一个参数时小括号也可省略;
3)右侧只有一条语句时,大括号和return都可省略;
4)右侧有多条语句,必须要有大括号。
4.Lambda表达式用在for循环中
String[] names={"Mike","Mary","Eric","Lucy","Rose"}; List<String> students=Arrays.asList(names); // 以前的循环方式 for (String student: students) { System.out.print(student+ "; "); }
//方式一: 使用 lambda 表达式以及函数操作 students.forEach((student) -> System.out.print(student+ "; "));
//方式二:使用双冒号操作符(Lambda表达式方法引用的第一种语法格式,下面会讲 ) students.forEach(System.out::println); |
5.Lambda表达式的方法引用(双冒号操作符)
当Lambda体中的内容已有方法实现,此时可以使用方法引用,有三种语法格式:
1)对象::实例方法名
例:Consumer<String> con=(x)->System.out.println(x);可以写为:
Consumer<String> con=System.out::println;//System.out即PrintWriter对象
2)类::静态方法名
例:Comparator<Integer> com=(x,y)->Integer.compare(x,y);可以写为:
Comparator<Integer> com=Integer::compare;
3)类::实例方法名
例:BiPredicate<String,String> bp=(x,y)->x.equals(y);
BiPredicate<String,String> bp=String::equals;
注意:只有当Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用第3)种语法格式类::实例方法名。如上述例子中第一个参数x是实例方法x.equals(y)的调用者,第二个参数y是方法的参数。
数据流Stream
1.流的获取
1)通过集合Collection获取
List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
Stream<Integer> stream = list.stream(); |
2)通过数组获取
String[] array = {"are","you","ok"};
Stream<String> stream = Array.stream(array); //对于基本类型数组的处理 int[] array = {1,2,3,4,5};
//此时Arrays.stream(array)获取的是一个IntStream对象 Stream<Integer> stream = Arrays.stream(array).boxed(); |
3)通过值获取(此方式存在缺陷)
Stream<String> stream = Stream.of("are","you","ok"); |
2.筛选filter
filter函数接收一个Lambda表达式作为参数,该表达式返回boolean,在执行过程中,流将元素逐一输送给filter,并筛选出执行结果为true的元素;
//筛选出列表中的非空项
List<String> list = Arrays.asList("are","you","","ok");
List<String> filted = list.stream() .filter(x-> !x.empty()) .collect(Collectors.toList()); |
3.去重、截取、跳过
去重distinct:
List<String> list = Arrays.asList("are","you","you","ok"); List<String> distincted = list.stream() .distinct() .collect(Collectors.toList()); |
截取流的前n个元素: .limit(3)
跳过流的前n个元素: .skip(2)
4.映射 map
对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。流会将每一个元素输送给map函数,并执行map中的Lambda表达式,最后将执行结果存入一个新的流中。
如:将 list 中每一个 Integer类型元素自增后转化为 String类型
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<String> result = list.stream() .map(x->String.valueOf(++x)) .collect(Collectors.toList()); |
5.合并多个流 flatMap
List<String> list1 = ..... List<String> list2 = ... List<List<String>> list = Arrays.asList(list1,list2);
//将list中的list1,list2合并为一个List<String> List<String> listsum = list.stream() .flatMap(List::stream) .collect(Collectors.toList());
以下一个实际的应用例子:列出 list 中各不相同的单词 List<String> list = new ArrayList<String>(); list.add("I am a boy"); list.add("I love the girl"); list.add("But the girl loves another girl");
list.stream().map(line->line.split(" ")) //将每一个项分词,并映射为数组 //将每一个分项数组组合并到主流中,形成一个包含所有分项数组的总数组流 .flatMap(Arrays::stream) .distinct(); //去重 .forEach(System.out::println); //打印 |
6.匹配元素
1) 是否匹配任一元素:anyMatch
2) 是否匹配所有元素:allMatch
3) 是否未匹配所有元素:noneMatch
7.获取元素
1) 获取任一元素 :findAny
2) 获取第一个元素:findFirst
Optional 对象介绍
Optional是Java8新加入的一个容器,这个容器只存1个或0个元素,它用于防止出现NullpointException,它提供如下方法:
- isPresent() 判断容器中是否有值。
- ifPresent(Consume lambda) 容器若不为空则执行括号中的Lambda表达式。
- T get() 获取容器中的元素,若容器为空则抛出NoSuchElement异常。
- T orElse(T other) 获取容器中的元素,若容器为空则返回括号中的默认值。
8.遍历流 forEach
forEach方法遍历流中每个元素,参数为一个Lambda表达式,用于对每一个遍历的元素执行的操作;
//输出10个随机数 Random random = new Random(); random.ints().limit(10).forEach(System.out::println); |