何为Stream
关于Stream,官方的说法为:支持顺序和并行聚合操作的一系列元素。根据上述的描述,Stream主要的作用就是排序和对集合进行相关的操作。
通常来说,官方对于这块的说法有:
- 他是惰性的,只有当需要用到时才会对其进行处理。
- 大多数的操作行为都是通过用户参数指定的。
- 用户指定的参数行为不应对流本身进行修改。
- 大多数情况下必须时无状态的。
就是说,流指挥在需要时才会执行相关操作,大部分操作都必须由用户控制,用户不应当修改流本身,也不支持操作状态返回。
流的基本操作
获取一个流
流的获取可以由数据直接获取,也可以从一个集合中获取。
1. 从数据中获取
Stream<String> s = Stream.of("1", "2", "3", "4");
2. 从集合获取
List<String> list = Arrays.asList("1", "2", "3", "4", "5");
Stream<String> s = list.stream();
对流进行操作
排序
这里的排序其实和Collections.sort用法是一致的,如果不传比较器,对象必须是实现了Comparable接口才行。或者我们要传入一个Comparator的实现。
Stream<String> s = Stream.of("1", "3", "2", "4");
s.sorted().forEach(System.out::println);
输出的结果为
1
2
3
4
如果我们要想按我们自己的想法进行排序,那就要传入自己的算法:
例如下面一段:
Stream<String> s = Stream.of("11", "3", "2", "4");
s.sorted().forEach(System.out::println);
输出的结果为
11
2
3
4
如果我们想用数字的方式排序,那么我们应当这样:
Stream<String> s = Stream.of("11", "3", "2", "4");
s.sorted(Comparator.comparing(Integer::valueOf)).forEach(System.out::println);
输出的结果为
2
3
4
11
去重
Stream提供了内置的去重方法 Stream.distinct(),使用后可以对流中的元素进行去重处理,同时这个操作可以和上述的排序进行嵌套的操作。
例如如下例子:
Stream<String> s = Stream.of("1", "3", "1", "4", "1", "2");
s.distinct().sorted().forEach(System.out::println);
输出的结果为:
1
2
3
4
这个处理让我们去除了重复的元素之后再进行排序。
剔除数据
这个场景是指我们可以剔除满足指定条件的记录,例如我们要剔除流中值不为1的记录:
Stream<String> s = Stream.of("1", "3", "1", "4", "1", "2");
s.filter(s1 -> !"1".equals(s1)).forEach(System.out::println);
这样就会得到不为1的值:
3
4
2
对数据进行分页
Stream同时提供了skip和limit方法实现分页,这两个方法分别是跳过多少个值和取前几位。例如一个有7条的数据,我们想要获取第三页的数据(第5到第6条),那么就可以这样:
Stream<String> s = Stream.of("1", "2", "3", "4", "5", "6", "7");
s.skip(4).limit(2).forEach(System.out::println);
这个方法虽然可以进行分页,但是他不能多次分页,每次分页必须重新创建一个Stream对象,好处就是,分页时如果超出页数,不会报数组下标越界的错误。
累加器
对于我来说,累加器在实际场景中用的并不多,他主要用于求和运算,当然也可以用于字符串的累加等。下面就用几个例子来说明累加器的用途。这里就不做详细的阐述了。
对一系列的数字求和:
Stream<Integer> s = Stream.of(1, 2, 3, 4, 5, 6, 7);
System.out.println(s.reduce(0, (a, b) -> a + b)); // 结果为 28
对一系列的数字累乘:
Stream<Integer> s = Stream.of(1, 2, 3, 4, 5, 6, 7);
System.out.println(s.reduce(1, (a, b) -> a * b)); // 结果为 5040
字符串求和:
Stream<String> s = Stream.of("1", "2", "3", "4", "5", "6", "7");
System.out.println(s.reduce("", (a, b) -> a + b)); // 结果为 1234567
当然,上面的例子其实都可以没有初始值,这时返回的就是一个Optional对象,以字符串的例子为例,也可以这样写:
Stream<String> s = Stream.of("1", "2", "3", "4", "5", "6", "7");
System.out.println(s.reduce((a, b) -> a + b)); // 结果为 Optional[1234567]
最大与最小
用Stream.max和Stream.min方法可以取得流的最大与最小值。但这个必须传一个比较器。
日志
这里说的日志是指对Stream中的对象进行操作时进行的记录,通常我们不会用,常见于调试,方法为Stream.peek(Consumer),详细使用我就不细述了,就举一个例子:
Stream<Integer> s = Stream.of(1, 2, 3, 4, 5, 6, 7);
System.out.println(s.peek(System.out::println).reduce((a, b) -> a + b));
输出结果:
1
2
3
4
5
6
7
Optional[28]
转成集合
如果需要把流转换成集合,我们可以直接调用collect方法。这个也是Stream中最花里胡哨的方法,应该也是我们平时用到的最多的方法,经测试,效率比我们遍历处理会略高,但是并没有高太多。
流转list
Stream<String> s = Stream.of("11", "3", "2", "4");
List<String> list = s.sorted(Comparator.comparing(Integer::valueOf)).collect(Collectors.toList());
如果你需要把流转成其他的类型的list,那么你只需要这样:
Stream<String> s = Stream.of("11", "3", "2", "4");
Stream<Integer> list = s.sorted(Comparator.comparing(Integer::valueOf)).map(a -> Integer.valueOf(a));
流转set
Set<String> set = s.collect(Collectors.toSet());
流转Map
Map<String, Integer> map = s.collect(Collectors.toMap(s1 -> s1, Integer::parseInt));
这里前面的方法是用值生成map的key,后面的方法则是用值生成对应的map的值。
分组
Stream<Integer> s = Stream.of(1, 1, 2, 2, 3, 3, 4);
Map<Integer, List<Integer>> map = s.collect(Collectors.groupingBy(i->i));
System.out.println(map);
输出结果:
{1=[1, 1], 2=[2, 2], 3=[3, 3], 4=[4]}
结语
以上就是Stream的常用操作,当然它还有很多的用法没有介绍,这些用法在品是工作中比较少使用,如果感兴趣的,可以自己探索。