一、Stream概述
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
二、Stream 常用创建方法
1.stream(串行流)
为集合创建串行流
//串行
Stream<User> stream = list.stream();
2.parallelStream(并行流)
为集合创建并行流
//并行
Stream<User> stream = list.parallelStream();
无需等待上游类型 filter
| map
| flatMap
| peek
|
需要等待上游类型 skip
| distinct
| limit
| sorted
三、Stream 常用中间方法
都会返回一个新的 stream 流。
1. filter(过滤)
对流中的元素进行条件过滤
。
参数为一个Predicate
(断定函数),函数返回值类型为boolean
的方法。
//实现接口
Predicate<Object> predicate = new Predicate<>() {
@Override
public boolean test(Object o) {
return false;
}
};
//使用lambda表达式
Predicate<Integer> predicate = x -> x>5;
实例:
//筛选出年龄在21~25之间的用户
List<User> collect = userList.stream()
.filter(user -> user.getAge() > 20 && user.getAge() <= 25)
.collect(Collectors.toList());
2. map(映射)
改变
流中每一个元素的值。
参数为了一个Function
(函数) 有固定返回类型的方法。
//实现接口
Function<User, User> function = new Function<>() {
@Override
public User apply(User user) {
return null;
}
};
//使用lambda表达式
Function<User, User> function = user -> user.setName(user.getName()+" Nb");
实例:
//给每个用户名字后面加上“Nb”
List<User> collect = list.stream()
.map(user -> user.setName(user.getName()+" Nb"))
.collect(Collectors.toList());
3. flatMap(平铺映射)
通过某种方法将元素变成 stream,再将所有的 stream 组装成一个 stream,实现对元素的平铺
操作。
参数为一个 返回结果为 Stream 对象的 Function
。
实例:
//将两个 list 中的全部用户名字后面加上“Nb”
List<List<User>> list = new ArrayList<>();
List<User> collect = list.stream()
.flatMap(Collection::stream)
.map(user -> user.setName(user.getName()+" Nb"))
.collect(Collectors.toList());
4. peek(镜像)
类似于 map ,对每个元素执行一个动作
参数为一个Consumer
(消费函数),一个无返回的方法。
//实现接口
Consumer<User> consumer1 = new Consumer<>() {
@Override
public void accept(User user) {
}
};
//使用lambda表达式
Consumer<User> consumer = (User user) -> System.out.println("name:" + user.getName() + " phone:" + user.getPhone());
实例:
//输出用户名的手机号信息
List<User> collect = userList.stream()
.peek((User user) -> System.out.println("name:" + user.getName() + " phone:" + user.getPhone()))
.collect(Collectors.toList());
5. skip(跳跃)
跳过
前 n 个元素,只保留剩下的元素。
参数为一个 Long 类型,要跳过的元素个数。
实例:
//跳过前2个用户
List<User> collect = userList.stream().skip(2).collect(Collectors.toList());
6. distinct(去重)
将流中的元素进行去重
,只保留没有重复的元素。
无参数。
实例:
//用户列表进行去重
List<User> collect = userList.stream().distinct().collect(Collectors.toList());
7. limit(限制)
只保留
n 个元素。
参数为一个 Long 类型,要保留的元素个数。
实例:
//只保留 2 个用户
List<User> collect = userList.stream().limit(2).collect(Collectors.toList());
limit 搭配 skip 进行分页
处理:
//分页操作
List<User> collect = userList.stream()
.skip((long) pageNum * pageSize)
.limit(pageSize).collect(Collectors.toList());
8. sorted(排序)
对流中的元素按照一定条件进行排序
。
参数为一个 Comparator
比较接口,返回排序规则。
//实现接口
Comparator<User> comparator = new Comparator<>() {
@Override
public int compare(User o1, User o2) {
return Integer.compare(o2.getAge(), o1.getAge());
}
};
//比较器
Comparator<User> comparator = Comparator.comparingInt(User::getAge);
实例:
//按照用户名进行排序
List<User> collect = userList.stream()
.sorted(Comparator.comparing(User::getName))
.collect(Collectors.toList());
四、Stream 常用收集方法
1. count(统计)
对最后 stream 中的元素进行统计
。
无参数。会返回一个 long 类型数值。
实例:
//年龄大于21的人数
long count = userList.stream().filter(user -> user.getAge() > 21).count();
2. sum(求和)
对元素的某个属性进行求和
。
无参数。会返回一个 Integer 类型数值。
实例:
//年龄总和
Integer sum = userList.stream().mapToInt(User::getAge).sum();
3. max(最大值)
取出 stream 流中的最大值
。
参数为一个 Comparator
比较接口,返回比较大小的规则。
返回值为 public final class Optional<T>
//如果有值,可以执行一个 Consumer 消费函数
public void ifPresent(Consumer<? super T> action) {
if (value != null) {
action.accept(value);
}
}
//如果有值,执行一个消费函数,如果没有值执行另一个线程方法
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (value != null) {
action.accept(value);
} else {
emptyAction.run();
}
}
实例:
//年龄最大的用户
Optional<User> min = userList.stream().max(Comparator.comparing(User::getAge));
max.ifPresent(System.out::println);
4. min(最小值)
取出 stream 流中的最小值
。
参数为一个 Comparator
比较接口,返回比较大小的规则。
返回值为 public final class Optional<T>
实例:
//年龄最小的用户
Optional<User> min = userList.stream().min(Comparator.comparing(User::getAge));
min.ifPresentOrElse(System.out::println, () -> System.out.println("isNull"));
5. collect(收集)
5.1 toList/toSet/toMap(归集)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList
、toSet
和toMap
比较常用,另外还有toCollection
、toConcurrentMap
等复杂一些的用法。
实例:
//将排序后的流收集为一个 list 集合
List<User> list = userList.stream()
.sorted(Comparator.comparing(User::getAge))
.collect(Collectors.toList());
//将结果收集为 用户名:用户 类型的 map 集合
Map<String, User> map = userList.stream()
.collect(Collectors.toMap(User::getName, user -> user));
//将结果收集为一个无序的 set 集合
Set<User> set = userList.stream()
.filter(user -> user.getAge()>21)
.collect(Collectors.toSet());
5.2 count/averaging(统计)
Collectors
提供了一系列用于数据统计的静态方法:
- 计数:
count
- 平均值:
averagingInt
、averagingLong
、averagingDouble
- 最值:
maxBy
、minBy
- 求和:
summingInt
、summingLong
、summingDouble
- 统计以上所有:
summarizingInt
、summarizingLong
、summarizingDouble
实例:
//用户年龄的平均值
Double averagingIn = userList.stream().collect(Collectors.averagingInt(User::getAge));
//计数、求和、最小值、平均值、最大值
IntSummaryStatistics summarizingInt = userList.stream().collect(Collectors.summarizingInt(User::getAge));
System.out.println("平均值: "+averagingIn);
System.out.println("计数、求和、最小值、平均值、最大值: "+summarizingInt);
输出:
平均值: 21.4
求和: IntSummaryStatistics{count=5, sum=107, min=20, average=21.400000, max=22}
5.3 partitioningBy/groupingBy(分组)
分区
:将stream
按条件分为两个Map
。分组
:将集合分为多个Map。有单级分组和多级分组。
实例:
//根据性别进行分组
Map<Integer, List<User>> groupingBy = userList.stream().collect(Collectors.groupingBy(User::getSex));
//将年龄根据条件分为两组(一组为满足条件的,一组为不满足条件的)
Map<Boolean, List<User>> partitioningBy = userList.stream().collect(Collectors.partitioningBy(user -> user.getAge() > 21)
System.out.println("groupingBy: "+groupingBy);
System.out.println("partitioningBy: "+partitioningBy);
输出:
groupingBy
: {0
=[User(id=2, name=Zhao, age=22, phone=18137525590, sex=0)],1
=[User(id=1, name=Hou, age=22, phone=15136449091, sex=1)]}
partitioningBy
: {false
=[User(id=3, name=Shi, age=21, phone=15103832393, sex=1)],true
=[User(id=1, name=Hou, age=22, phone=15136449091, sex=1)]}
5.4 joining(接合)
joining
可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接
成一个字符串。
实例:
//将用户的名字用 - 连接
String joining = userList.stream().map(User::getName).collect(Collectors.joining("-"));
System.out.println("joining: "+joining);
输出:
joining: Hou-Zhao-Shi-Wang-Lv