JDK8中的Lambda和StreamApi
Posted 诺浅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK8中的Lambda和StreamApi相关的知识,希望对你有一定的参考价值。
Lambda
本文只是简单记录,供自己查阅,详细的还请看 恕我直言,你真的不会JAVA系列
(param1,param2,param3 ...,paramN)- > //代码块;
准备数据
数据会在下面多个例子中用到
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
StreamApi
filter
我理解为过滤、帅选
如下过滤元素中age大于70且为男性的元素
List<Employee> filtered = employees.stream()
.filter(e -> e.getAge() > 70 && e.getGender().equals("M"))
.collect(Collectors.toList());
还可以把上面的age大于70且性别为男性定义出来,这个叫谓词逻辑
public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
public static Predicate<Employee> genderM = x -> x.getGender().equals("M");
然后就可以使用
List<Employee> filtered = employees.stream()
.filter(Employee.ageGreaterThan70.and(Employee.genderM))
.collect(Collectors.toList());
除了and
还有or(交集)
和negate(取反)
List<Employee> filtered = employees.stream()
.filter(Employee.ageGreaterThan70.or(Employee.genderM))
.collect(Collectors.toList());
List<Employee> filtered = employees.stream()
.filter(Employee.ageGreaterThan70.or(Employee.genderM).negate())
.collect(Collectors.toList());
map、peek、flatMap
map
map可以理解为转换、映射
比如如下可以输出员工的name信息
employees.stream().map(e->e.getName).forEach(System.out::println)
或者把name转大写然后输出
employees.stream().map(String::toUpperCase).forEach(System.out::println)
peek
peek可以理解为无返回值的map,比如我们希望员工年龄+1,输出性别的全部子母,用peek可以如下实现
List<Employee> maped = employees.stream()
.peek(e ->
e.setAge(e.getAge() + 1);
e.setGender(e.getGender().equals("M")?"male":"female");
).collect(Collectors.toList());
用map则需要返回值
List<Employee> maped = employees.stream()
.map(e ->
e.setAge(e.getAge() + 1);
e.setGender(e.getGender().equals("M")?"male":"female");
return e;
).collect(Collectors.toList());
flatMap
map可以对管道流中的数据进行转换操作,但是如果管道中还有管道该如何处理?即:如何处理二维数组及二维集合类。实现一个简单的需求:将“hello”,“world”两个字符串组成的集合,元素的每一个字母打印出来。
如果用map会发现打印出来的依然是两个stream
List<String> words = Arrays.asList("hello", "word");
words.stream()
.map(w -> Arrays.stream(w.split(""))) //[[h,e,l,l,o],[w,o,r,l,d]]
.forEach(System.out::println);
如果是用flatMap就可以实现
words.stream()
.flatMap(w -> Arrays.stream(w.split(""))) // [h,e,l,l,o,w,o,r,l,d]
.forEach(System.out::println);
Limit、Skip、Distinct、parallel
limit
取前面两个元素
List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
.limit(2)
.collect(Collectors.toList()); // ["Monkey", "Lion"]
skip
取除前面两个元素之外的元素
List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
.skip(2)
.collect(Collectors.toList()); // ["Giraffe", "Lemur"]
Distinct 去重
List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.distinct()
.collect(Collectors.toList()); //["Monkey", "Lion", "Giraffe", "Lemur"]
parallel 并行
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.parallel()
.forEach(System.out::println); // ["Monkey", "Lion", "Lemur", "Giraffe", "Lion"]
由于不能保证元素的顺序性,所以最好不要用什么limit这种,不然你limit的元素是不确定的
sorted排序
List<String> cities = Arrays.asList(
"Milan",
"london",
"San Francisco",
"Tokyo",
"New Delhi"
);
使用Comparator.naturalOrder()字母自然顺序排序,结果是:[Milan, New Delhi, San Francisco, Tokyo, london]
cities.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
查找与匹配
anyMatch
boolean isExistAgeThan70 = employees.stream().anyMatch(Employee.ageGreaterThan70);
// 或者
boolean isExistAgeThan72 = employees.stream().anyMatch(e -> e.getAge() > 70);
anyMatch,判断Stream流中是否包含某一个“匹配规则”的元素。这个匹配规则可以是lambda表达式或者谓词。
allMatch
boolean isExistAgeThan10 = employees.stream().allMatch(e -> e.getAge() > 10);
allMatch匹配规则函数:判断是够Stream流中的所有元素都符合某一个"匹配规则"。
noneMatch
oolean isExistAgeLess18 = employees.stream().noneMatch(e -> e.getAge() < 18);
noneMatch匹配规则函数:判断是否Stream流中的所有元素都不符合某一个"匹配规则"。
Optional 非空处理
从列表中按照顺序查找第一个年龄大于40的员工。
Optional<Employee> employeeOptional
= employees.stream().filter(e -> e.getAge() > 40).findFirst();
System.out.println(employeeOptional.get());
Optional类代表一个值存在或者不存在。在java8中引入,这样就不用返回null了。
- isPresent() 将在 Optional 包含值的时候返回 true , 否则返回 false 。
- ifPresent(Consumer block) 会在值存在的时候执行给定的代码块。
- T get() 会在值存在时返回值,否则?出一个 NoSuchElement 异常。
- T orElse(T other) 会在值存在时返回值,否则返回一个默认值。
reduce 规约
求和
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
int result = numbers
.stream()
.reduce(0, (subtotal, element) -> subtotal + element);
System.out.println(result); //21
int result = numbers
.stream()
.reduce(0, Integer::sum);
System.out.println(result); //21
字符串拼接
List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
String result = letters
.stream()
.reduce("", (partialString, element) -> partialString + element);
System.out.println(result); //abcde
String result = letters
.stream()
.reduce("", String::concat);
System.out.println(result); //ancde
复杂对象归约
Integer total = employees.stream().map(Employee::getAge).reduce(0,Integer::sum);
System.out.println(total); //346
这里用了map先把流中的对象转成了Age,也可以不转直接用reduce
因为Stream流中的元素是Employee,累加器的返回值是Integer,所以二者的类型不匹配。这种情况下可以使用Combiner合并器对累加器的结果进行二次归约,相当于做了类型转换。
Integer total3 = employees.stream()
.reduce(0,(totalAge,emp) -> totalAge + emp.getAge(),Integer::sum); //注意这里reduce方法有三个参数
System.out.println(total); //346
在进行并行流计算的时候,可能会将集合元素分成多个组计算。为了更快的将分组计算结果累加,可以使用合并器。
Integer total2 = employees
.parallelStream()
.map(Employee::getAge)
.reduce(0,Integer::sum,Integer::sum); //注意这里reduce方法有三个参数
System.out.println(total); //346
ForEach和ForEachOrdered
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.parallel()
.forEach(System.out::println);
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.parallel()
.forEachOrdered(System.out::println);
- parallel()函数表示对管道中的元素进行并行处理,而不是串行处理,这样处理速度更快。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证
- forEachOrdered从名字上看就可以理解,虽然在数据处理顺序上可能无法保障,但是forEachOrdered方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致。
元素的收集collect
收集为set
Set<String> collectToSet = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.collect(Collectors.toSet());
收集为list
List<String> collectToList = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).collect(Collectors.toList());
通用的收集方式
LinkedList<String> collectToCollection = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).collect(Collectors.toCollection(LinkedList::new));
、、
当然你还可以使用诸如LinkedHashSet::new和PriorityQueue::new将数据元素收集为其他的集合类型,这样就比较通用了。
收集到Array
String[] toArray = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
) .toArray(String[]::new);
收集到Map
使用Collectors.toMap()方法将数据元素收集到Map里面,但是出现一个问题:那就是管道中的元素是作为key,还是作为value。我们用到了一个Function.identity()方法,该方法很简单就是返回一个“ t -> t ”(输入就是输出的lambda表达式)。另外使用管道流处理函数distinct()来确保Map键值的唯一性。
Map<String, Integer> toMap = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.distinct()
.collect(Collectors.toMap(
Function.identity(), //元素输入就是输出,作为key
s -> (int) s.chars().distinct().count()// 输入元素的不同的字母个数,作为value
));
// 最终toMap的结果是: Monkey=6, Lion=4, Lemur=5, Giraffe=6
分组收集groupingBy
Collectors.groupingBy用来实现元素的分组收集,下面的代码演示如何根据首字母将不同的数据元素收集到不同的List,并封装为Map。
Map<Character, List<String>> groupingByList = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.collect(Collectors.groupingBy(
s -> s.charAt(0) , //根据元素首字母分组,相同的在一组
// counting() // 加上这一行代码可以实现分组统计
));
// 最终groupingByList内的元素: G=[Giraffe], L=[Lion, Lemur, Lion], M=[Monkey]
//如果加上counting() ,结果是: G=1, L=3, M=1
排序Map
HashMap的merge()函数
该函数应用场景就是当Key重复的时候,如何处理Map的元素值。这个函数有三个参数:
- 参数一:向map里面put的键
- 参数二:向map里面put的值
- 参数三:如果键发生重复,如何处理值。可以是一个函数,也可以写成lambda表达式。
String k = "key";
HashMap<String, Integer> map = new HashMap<String, Integer>()
put(k, 1);
;
map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);
看上面一段代码,我们首先创建了一个HashMap,并往里面放入了一个键值为k:1的元素。当我们调用merge函数,往map里面放入k:2键值对的时候,k键发生重复,就执行后面的lambda表达式。表达式的含义是:返回旧值oldVal加上新值newVal(1+2),现在map里面只有一项元素那就是k:3。
按Map的键排序
// 创以上是关于JDK8中的Lambda和StreamApi的主要内容,如果未能解决你的问题,请参考以下文章