Java Stream流操作

前言

Stream流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算。

Stream流是对集合(Collection)对象功能的增强,与Lambda表达式结合,可以提高编程效率、间接性和程序可读性。

而且有句话说的好,如果会stream流,那么你和大数据就只隔了一层纱。所以对它的学习还是有必要的。

Stream流的特点

1、代码简洁:函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环

2、多核友好:Java函数式编程使得编写并行程序如此简单,就是调用一下方法

流的创建

1.stream创建

1
Stream<Integer> stream1 = Stream.of(1,2,3,4,5);

2.集合创建

1
2
3
4
5
6
7
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.add(4);
integerList.add(5);
Stream<Integer> listStream = integerList.stream();

3.数组创建

1
2
int[] intArr = {1, 2, 3, 4, 5};
IntStream arrayStream = Arrays.stream(intArr);

4.文件创建

1
2
3
4
5
try {
Stream<String> fileStream = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
}

5.函数创建(iterator和generator)

iterator

1
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(5);

iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数

generator

1
2
3
4
5
Stream<Integer> limit = Stream.generate(Math::random).limit(5).map(i->100*i).map(Double::intValue);
List<Integer> integerList = limit.collect(Collectors.toList());
for (Integer integer : integerList) {
System.out.println(integer);
}

generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断

中间操作符

直接上代码,假设有这么一个List集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
List<User> userList = getUserList();
}

private static List<User> getUserList() {
List<User> userList = new ArrayList<>();

userList.add(new User(1,"张三",18,"上海"));
userList.add(new User(2,"王五",16,"上海"));
userList.add(new User(3,"李四",20,"上海"));
userList.add(new User(4,"张雷",22,"北京"));
userList.add(new User(5,"张超",15,"深圳"));
userList.add(new User(6,"李雷",24,"北京"));
userList.add(new User(7,"王爷",21,"上海"));
userList.add(new User(8,"张三丰",18,"广州"));
userList.add(new User(9,"赵六",16,"广州"));
userList.add(new User(10,"赵无极",26,"深圳"));

return userList;
}

filter过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//1、filter:输出ID大于6的user对象
List<User> filetrUserList = userList.stream()
.filter(user -> user.getId() > 6).collect(Collectors.toList());
filetrUserList.forEach(System.out::println);
//2、map
List<String> mapUserList = userList.stream()
.map(user -> user.getName() + "用户").collect(Collectors.toList());
mapUserList.forEach(System.out::println);
//3、distinct:去重,根据流所生成元素的hashCode和equals方法实现
List<String> distinctUsers = userList.stream().map(User::getCity).distinct()
.collect(Collectors.toList());
distinctUsers.forEach(System.out::println);
//4、sorted:排序,根据名字倒序
userList.stream().sorted(Comparator.comparing(User::getName).reversed())
.collect(Collectors.toList()).forEach(System.out::println);
//5、limit:取前5条数据
userList.stream().limit(5).collect(Collectors.toList()).forEach(System.out::println);
//6、skip:跳过第几条取后几条
userList.stream().skip(7).collect(Collectors.toList()).forEach(System.out::println);
//7、flatMap:数据拆分一对多映射,即扁平化为一个流
//比如city = "太原,西安",那么就会把一个user映射成太原,西安两个city
userList.stream().flatMap(user -> Arrays.stream(user.getCity().split(",")))
.forEach(System.out::println);
//8、peek:对元素进行遍历处理,用于在处理流的过程中执行额外的操作,例如打印、记录日志等。每个用户ID加1输出
userList.stream().peek(user -> user.setId(user.getId()+1)).forEach(System.out::println);

终端操作符

Stream流执行完终端操作之后,无法再执行其他动作,否则会报状态异常,提示该流已经被执行操作或者被关闭,想要再次执行操作必须重新创建Stream流

一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。

终端操作的执行,才会真正开始流的遍历。如 count、collect 等

★collect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//1、collect:收集器,将流转换为其他形式
Set set = userList.stream().collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("--------------------------");
List list = userList.stream().collect(Collectors.toList());
list.forEach(System.out::println);
// 用Stream流查出所有城市对应的一个User
// toMap的重载版本,其中第三个参数是一个合并函数。该合并函数在遇到重复的键时被调用,以解决冲突。在这种情况下,合并函数(u1, u2) -> u1简单地选择保留旧值,即保留先遍历到的User对象。
Map<String, User> cityToUserMap = userList.stream()
.collect(Collectors.toMap(User::getCity, user -> user, (u1, u2) -> u1));
// 将用户所在城市,以指定分隔符链接成字符串
String joinCity = userList.stream().map(User::getCity).collect(Collectors.joining("||"));
// 按城市对用户进行分组
Map<String,List<User>> groupCity = userList.stream().collect(Collectors.groupingBy(User::getCity));

foreach

1
2
3
//2、forEach:遍历流
userList.stream().forEach(user -> System.out.println(user));
userList.stream().filter(user -> "上海".equals(user.getCity())).forEach(System.out::println);

findFirst、findAny

返回第一个元素、返回当前流中的任意元素

配合Optional类中的orElse

1
2
3
4
User a =  list.stream().filter(userT-> userT.getAge() == 12).findFirst().orElse(getMethod("a"));
User b = list.stream().filter(userT11-> userT11.getAge() == 12).findFirst().
orElseGet(()->getMethod("b"));

count、sum、min、max

类似SQL聚合函数

anyMatch、allMatch、noneMatch

检查是否至少匹配一个元素、检查是否匹配所有元素、检查是否没有匹配的元素。返回boolean

reduce

可以将流中元素反复结合起来,得到一个值

1
2
3
4
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
sum.ifPresent(System.out::println); // 15