Java8新特性之四:Stream API
Java8的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。 Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作,在新版的JPA【连接数据库】中,也已经加入了Stream。
Stream API给我们操作集合带来了强大的功用,同时Stream API操作简单,容易上手。
例如
@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
Stream<User> readAllByFirstnameNotNull();
@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
Stream的操作步骤
Stream有如下三个操作步骤:
一、创建Stream
从一个数据源,如集合、数组中获取流。
二、中间操作
一个操作的中间链,对数据源的数据进行操作。
三、终止操作
一个终止操作,执行中间操作链,并产生结果。
注意:对流的操作完成后需要进行关
举个简单的例子:
假设有一个Person类和一个Person列表,现在有两个需求:1)找到年龄大于18岁的人并输出;2)找出所有中国人的数量。
@Data
class Person {
private String name;
private Integer age;
private String country;
private char sex;
public Person(String name, Integer age, String country, char sex) {
this.name = name;
this.age = age;
this.country = country;
this.sex = sex;
}
}
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("向天笑",20,"中国",'M'));
personList.add(new Person("李康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));
在JDK8以前,我们可以通过遍历列表来完成。但是在有了Stream API后,可以这样来实现:
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪", 18, "中国", 'F'));
personList.add(new Person("Tom", 24, "美国", 'M'));
personList.add(new Person("Harley", 22, "英国", 'F'));
personList.add(new Person("向天笑", 20, "中国", 'M'));
personList.add(new Person("李康", 22, "中国", 'M'));
personList.add(new Person("小梅", 20, "中国", 'F'));
personList.add(new Person("何雪", 21, "中国", 'F'));
personList.add(new Person("李康", 22, "中国", 'M'));
// 1)找到年龄大于18岁的人并输出;
//创建stream对象 boolean test(T t); void accept(T t);
personList.stream().filter(t->t.getAge()>20).forEach(t->System.out.println(t));
// lamdba 里面的方法引用
personList.stream().filter(t->t.getAge()>20).forEach(System.out::println);
// 2)找出所有中国人的数量。 count()计算数量
long c = personList.stream().filter(t -> t.getCountry().equals("中国")).count();
System.out.println(c);
}
在这个例子中,personList.stream()是创建流,filter()属于中间操作,forEach、count()是终止操作。
Stream中间操作–筛选与切片 与parallelStream并行流
filter:接收Landba,从流中排除某些操作
limit:截断流,使其元素不超过给定对象
skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中不足n个,则返回一个空流,与limit(n)互补
distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
limit举例
需求,从Person列表中取出两个女性。
// 从Person列表中取出两个女性。 limit
personList.stream().filter(t -> t.getSex() == 'F').limit(2).forEach(System.out::println);
skip举例
从Person列表中从第2个女性开始,取出所有的女性。
// 从Person列表中从第2个女性开始,取出所有的女性。 skip(1)表示跳过第一个
personList.stream().filter(t->t.getSex()=='F').skip(1).forEach(System.out::println);
distinct举例
// 去重
personList.stream().distinct().forEach(System.out::println);
Stream中间操作–映射
- map–接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap–接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
map举例
比如,我们用一个PersonCountry类来接收所有的国家信息
// 比如,我们用一个PersonCountry类来接收所有的国家信息 R apply(T t);
personList.stream().map(t->t.getCountry()).forEach(System.out::println);
接收所有的姓名和国家 首先封装一个两个字段的实体类
// 接收所有的姓名和国家 首先封装一个两个字段的实体类
personList.stream().map(t->new NameAndCountry(t.getName(),t.getCountry())).forEach(System.out::println);
@Data
@AllArgsConstructor
@NoArgsConstructor
class NameAndCountry{
private String name;
private String country;
}
对前面的personList按照年龄从小到大排序,年龄相同,再按照姓名排序
//对前面的personList按照年龄从小到大排序,年龄相同,再按照姓名排序
//使用无参必须该类实现自然排序
// personList.stream().sorted();
// int compare(T o1, T o2);
// 第一种方法
personList.stream().sorted((o1, o2) -> {
if (o1.getAge() == o2.getAge()) {
return o1.getName().compareTo(o2.getName());
}
return o1.getAge() - o2.getAge();
}).forEach(System.out::println);
System.out.println("-----------------");
//第二种方法 使用三目运算符
personList.stream().sorted((o1, o2) ->o1.getAge()==o2.getAge()?o1.getName().compareTo(o2.getName()):o1.getAge()-o2.getAge()).forEach(System.out::println);
总结
1. filter(Predicate)
2. limit(long)
3. skip(long)
4. distinct();
5. map(Function)
6. sorted() | sorted(Comparator)
终止操作–查找与匹配
- allMatch–检查是否匹配所有元素
- anyMatch–检查是否至少匹配一个元素
- noneMatch–检查是否没有匹配所有元素
- findFirst–返回第一个元素
- findAny–返回当前流中的任意元素
- count–返回流中元素的总个数
- max–返回流中最大值
- min–返回流中最小值
这些方面在Stream类中都有说明,这里不一一举例,只对allMatch、max各举一例进行说明。
allMatch检查是否至少匹配一个元素
判断personList中的人是否都是大于18岁的
//检查是否至少匹配一个元素
boolean b = personList.stream().anyMatch(item -> item.getAge() >18);
System.out.println(b);
判断personList中的人是否都是成年人
// 判断personList中的人是否都是中国人:
boolean b1 = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
System.out.println(b1);
findAny返回当前流中的任意元素
//返回当前流中的任意元素
Optional<Person> any = personList.stream().findAny();
System.out.println(any);
//返回当前流中的任意元素
// Person person = personList.stream().findAny().get();
Person person = personList.parallelStream().findAny().get();
System.out.println(person);
max-返回流中最大值
personList中找到年龄最大的
// 找到年龄最大的 int compare(T o1, T o2);
Optional<Person> max = personList.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println(max);
归约
Stream API的归约操作可以将流中元素反复结合起来,得到一个值,有
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
求一个1到100的和
第一种方法:
用到了reduce第二个方法:T reduce(T identity, BinaryOperator accumulator)
// 求1-100的和 规约
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new Random().nextInt(10));
}
System.out.println(list);
// R apply(T t, T u,T r);
Optional<Integer> reduce = list.stream().reduce((t, u) -> t + u);
System.out.println(reduce);
把这个动作拆解一下,其运算步骤模拟如下:
0 (1,2) -> 1 + 2 + 0
3 (3,4) -> 3 + 4 + 3
10 (5,6) -> 5 + 6 + 10
其运算步骤是,每次将列表的两个元素相加,并将结果与前一次的两个元素的相加结果进行累加,因此,在开始时,将identity设为0,因为第1个元素和第2个元素在相加的时候,前面还没有元素操作过
第二种方法:
Integer[] list = {1, 2, 3, 4, 5};
System.out.println(Arrays.asList(list));
Arrays.stream(list).reduce((t,u)->{
System.out.println(t+"-----"+u);
return t+u;
});
System.out.println(Arrays.asList(list));
求所有人的年龄之和
// 求所有人的年龄之和
Optional<Integer> reduce = personList.stream().map(item -> item.getAge()).reduce((t, u) -> t + u);
System.out.println(reduce);
求所有人的平均年龄
Collectors.averagingDouble
//averagingDouble
Double collect1 = personList.stream().collect(Collectors.averagingDouble(t -> t.getAge()));
System.out.println(collect1);
求所有人的年龄的集合
collect(Collectors.toList())
List<Integer> collect = personList.stream().map(item -> item.getAge()).collect(Collectors.toList());
求集合中所有人的最大年龄
// 求出集合中最大年龄
Optional<Person> collect2 = personList.stream().collect(Collectors.maxBy((o1, o2) -> o1.getAge() - o2.getAge()));
System.out.println(collect2);
最小年龄类型。
还有其他很操作,可以参考java.util.stream.Collectors。
注意流的关闭
求集合中所有人的最小年龄。
try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
final Optional<Integer> minAge = integerStream.collect(Collectors.minBy(Integer::compareTo));
System.out.println(minAge.get());
}
最好将流的操作放到try-with-resources,本章前面内容为了方便,没有放到try-with-resources中。
完整测试代码
package com.zz.lamdab.lamdba04;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class Deom1 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪", 18, "中国", 'F'));
personList.add(new Person("Tom", 24, "美国", 'M'));
personList.add(new Person("Harley", 22, "英国", 'F'));
personList.add(new Person("向天笑", 20, "中国", 'M'));
personList.add(new Person("李康", 22, "中国", 'M'));
personList.add(new Person("小梅", 20, "中国", 'F'));
personList.add(new Person("何雪", 21, "中国", 'F'));
personList.add(new Person("李康", 22, "中国", 'M'));
// 1)找到年龄大于18岁的人并输出;
//创建stream对象 boolean test(T t); void accept(T t);
personList.stream().filter(t -> t.getAge() > 20).forEach(t -> System.out.println(t));
// lamdba 里面的方法引用
personList.stream().filter(t -> t.getAge() > 20).forEach(System.out::println);
// 2)找出所有中国人的数量。 count()计算数量
long c = personList.stream().filter(t -> t.getCountry().equals("中国")).count();
System.out.println(c);
// 从Person列表中取出两个女性。 limit
personList.stream().filter(t -> t.getSex() == 'F').limit(2).forEach(System.out::println);
// 从Person列表中从第2个女性开始,取出所有的女性。 skip(1)表示跳过第一个
personList.stream().filter(t -> t.getSex() == 'F').skip(1).forEach(System.out::println);
System.out.println("-----------------");
// 去重
personList.stream().distinct().forEach(System.out::println);
System.out.println("-----------------");
// 比如,我们用一个PersonCountry类来接收所有的国家信息 R apply(T t);
personList.stream().map(t -> t.getCountry()).forEach(System.out::println);
// 接收所有的姓名和国家 首先封装一个两个字段的实体类
personList.stream().map(t -> new NameAndCountry(t.getName(), t.getCountry())).forEach(System.out::println);
System.out.println("-----------------");
//对前面的personList按照年龄从小到大排序,年龄相同,再按照姓名排序
//使用无参必须该类实现自然排序
// personList.stream().sorted();
// int compare(T o1, T o2);
// 第一种方法
personList.stream().sorted((o1, o2) -> {
if (o1.getAge() == o2.getAge()) {
return o1.getName().compareTo(o2.getName());
}
return o1.getAge() - o2.getAge();
}).forEach(System.out::println);
System.out.println("-----------------");
//第二种方法 使用三目运算符
personList.stream().sorted((o1, o2) ->o1.getAge()==o2.getAge()?o1.getName().compareTo(o2.getName()):o1.getAge()-o2.getAge()).forEach(System.out::println);
System.out.println("-----------------");
//返回当前流中的任意元素
Optional<Person> any = personList.stream().findAny();
System.out.println(any);
System.out.println("-----------------");
// 检查是否至少匹配一个元素
boolean b = personList.stream().anyMatch(item -> item.getAge() >18);
System.out.println(b);
// 判断personList中的人是否都是中国人:
boolean b1 = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
System.out.println(b1);
//返回当前流中的任意元素
// Person person = personList.stream().findAny().get();
Person person = personList.parallelStream().findAny().get();
System.out.println(person);
// 找到年龄最大的 int compare(T o1, T o2);
Optional<Person> max = personList.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println(max);
// 求所有人的年龄之和
Optional<Integer> reduce = personList.stream().map(item -> item.getAge()).reduce((t, u) -> t + u);
System.out.println(reduce);
List<Integer> collect = personList.stream().map(item -> item.getAge()).collect(Collectors.toList());
//averagingDouble
Double collect1 = personList.stream().collect(Collectors.averagingDouble(t -> t.getAge()));
System.out.println(collect);
System.out.println(collect1);
// 求出集合中最大年龄
Optional<Person> collect2 = personList.stream().collect(Collectors.maxBy((o1, o2) -> o1.getAge() - o2.getAge()));
System.out.println(collect2);
// 注意流的关闭
// Stream<Integer> integerStream = personList.stream().map(Person::getAge);
try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
final Optional<Integer> minAge = integerStream.collect(Collectors.minBy(Integer::compareTo));
System.out.println(minAge.get());
}
}
}
@Data
class Person {
private String name;
private Integer age;
private String country;
private char sex;
public Person(String name, Integer age, String country, char sex) {
this.name = name;
this.age = age;
this.country = country;
this.sex = sex;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class NameAndCountry {
private String name;
private String country;
}