Java8新特性Stream详解
一、概念
1、Stream的定义
Stream(流)是一种用于处理集合数据的抽象概念。它可以让我们以一种类似于流水线的方式对数据进行操作和处理。Stream API 是 Java 8 引入的一个功能强大的工具,它提供了一种简洁而高效的方式来处理集合数据。通过使用 Stream,我们可以对集合进行过滤、映射、排序、聚合等操作,而无需编写繁琐的循环和条件语句。Stream 的操作可以串行执行,也可以并行执行,从而提高处理大量数据的效率。
2、Stream 的操作分类
1)中间操作
用于对流中的元素进行处理和转换,但并不会触发流的遍历和处理,会返回一个新的Stream流,可以继续进行其他的中间操作或终端操作。
通过组合不同的中间操作,可以构建出一个流水线,以实现复杂的数据处理逻辑。但需要注意的是,中间操作只有在遇到终端操作时才会被触发执行。
中间操作包括有状态和无状态操作两种:
- 有状态操作:操作需要维护状态来正确执行,每个元素的处理可能依赖于其他元素的状态或上下文。
例如,sorted和distinct操作,是需要维护一个状态,一个是记录位置,一个是记录是否出现过。 - 无状态操作:每个元素的处理都是独立的,不依赖于其他元素的状态。
例如,filter、map和flatMap,只是根据输入元素生成输出元素,而不会受到其他元素的影响。
2)终止操作
用于触发流的遍历和处理,并产生最终的结果或副作用。在流的最后调用,执行终止操作后,流将无法再使用。
终止操作包括短路操作和非短路操作两种:
- 短路操作:处理元素时,满足某个条件就立即返回结果,无需处理所有元素。
例如,findFirst、findAny、anyMatch和allMatch - 非短路操作:指必须处理所有元素才能得到最终结果;
例如,forEach、reduce和collect
二、操作
先获取流,然后可以执行各种中间操作,最终终止操作。
1、获取流
获取流的方式,官方文档内容如下:
Streams can be obtained in a number of ways. Some examples include:
From a Collection via the and methods;stream()parallelStream()
From an array via Arrays.stream(Object[]);
From static factory methods on the stream classes, such as Stream.of(Object[]), IntStream.range(int, int) or Stream.iterate(Object, UnaryOperator);
The lines of a file can be obtained from BufferedReader.lines();
Streams of file paths can be obtained from methods in Files;
Streams of random numbers can be obtained from Random.ints();
Numerous other stream-bearing methods in the JDK, including BitSet.stream(), Pattern.splitAsStream(java.lang.CharSequence), and JarFile.stream().
也就是可以从一个数据源,如集合、数组中获取流
1)从集合(Collection)获取
可以使用集合的stream()方法来获取一个Stream对象,例如:List、Set、Map
List<Integer> list = Arrays.asList(1, 2, 3);
// 通过List获取
Stream<Integer> stream1 = list.stream();
Set<Integer> set = new HashSet<>() {{
add(1);
add(2);
add(3);
}};
// 通过Set获取
Stream<Integer> stream2 = set.stream();
Map<String, String> map = new HashMap<>();
// 通过Map.entrySet获取
Stream<Map.Entry<String, String>> stream3 = map.entrySet().stream();
// 通过Map.keySet获取
Stream<String> stream4 = map.keySet().stream();
// 通过Map.values获取
Stream<String> stream5 = map.values().stream();
2)从数组(Array)获取
可以使用Arrays.stream()方法来获取一个Stream对象,例如:
Integer[] array = {1, 2, 3};
Stream<Integer> stream = Arrays.stream(array);
3)使用Stream的静态方法获取
Stream类提供了一些静态方法来获取Stream对象,例如:
Stream<Integer> stream = Stream.of(1, 2, 3);
IntStream range = IntStream.range(1, 100);
Stream<Integer> stream2 = Stream.iterate(0, n -> n + 2).limit(10);
Stream<Double> stream3 = Stream.generate(Math::random).limit(5);
4)从文件获取
可以使用java.nio.file.Files类的lines()方法来获取一个Stream对象,读取文件中的行,例如:
Path path = Paths.get("G:\\output.txt");
Stream<String> stream = Files.lines(path);
以上是一些常用的获取方式
2、中间操作
操作的中间环节,对数据源的数据进行操作,当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”
中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线
比如map (mapToInt, flatMap 等)、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered。
我们以下面这个List来演示
List<Forlan> list = new ArrayList<Forlan>() {{
add(new Forlan("张三", 177, 121));
add(new Forlan("李四", 166, 140));
add(new Forlan("王五", 189, 160));
add(new Forlan("张大", 177, 150));
add(new Forlan("林黄", 160, 110));
}};
1)filter
filter(Predicate predicate):根据指定的条件筛选元素。
list.stream().filter(p -> p.getName().contains("张")).forEach(System.out::println);
Forlan{name='张三', height=177, weight=121}
Forlan{name='张大', height=177, weight=150}
2)map
map(Function<T, R> mapper):将每个元素映射为另一个元素。
list.stream().map(Forlan::getName).forEach(System.out::println);
张三
李四
王五
张大
林黄
3)flatMap
flatMap(Function<T, Stream> mapper):将每个元素映射为一个Stream对象,并将所有Stream对象的元素合并为一个Stream对象。
list.stream().flatMap(p -> Arrays.asList(p.getHeight(), p.getWeight()).stream()).forEach(System.out::println);
177
121
166
140
189
160
177
150
160
110
4)distinct
distinct():去除重复的元素。
list.stream().map(Forlan::getHeight).distinct().forEach(System.out::println);
177
166
189
160
5)sorted
sorted():对元素进行排序。
list.stream().sorted((a, b) -> a.getHeight() - b.getHeight()).forEach(System.out::println);
按照height进行升序,运行结果如下:
Forlan{name='林黄', height=160, weight=110}
Forlan{name='李四', height=166, weight=140}
Forlan{name='张三', height=177, weight=121}
Forlan{name='张大', height=177, weight=150}
Forlan{name='王五', height=189, weight=160}
6)peek
peek(Consumer action):对每个元素执行指定的操作,不会改变Stream中的元素,主要用于调试和观察Stream中的元素
list.stream().peek(p -> System.out.println(p.getHeight())).map(p -> p.getHeight() % 2 == 0).forEach(System.out::println);
观察元素是否是偶数,运行结果如下:
177
false
166
true
189
false
177
false
160
true
7)limit
limit(long maxSize):限制Stream中元素的数量。
list.stream().limit(1).forEach(System.out::println);
限制返回一个元素,运行结果如下:
Forlan{name='张三', height=177, weight=121}
8)skip
skip(long n):跳过Stream中的前n个元素。
list.stream().skip(2).limit(2).forEach(System.out::println);
跳过2个元素,返回2个元素,运行结果如下:
Forlan{name='王五', height=189, weight=160}
Forlan{name='张大', height=177, weight=150}
3、终止操作
执行操作,返回结果
当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终止操作
比如:forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator。
1)forEach
forEach(Consumer<? super T> action):对流中的每个元素执行指定的操作,没有返回值。
list.stream().forEach(System.out::println);
输出每个元素,运行结果如下:
Forlan{name='张三', height=177, weight=121}
Forlan{name='李四', height=166, weight=140}
Forlan{name='王五', height=189, weight=160}
Forlan{name='张大', height=177, weight=150}
Forlan{name='林黄', height=160, weight=110}
2)toArray
toArray():将流中的元素转换为数组,返回一个包含流中所有元素的数组。
例如,將List转为数组
Forlan[] array = list.stream().toArray(Forlan[]::new);
3)reduce
reduce(T identity, BinaryOperator accumulator):将流中的元素按照指定的操作进行归约,返回一个值。identity是初始值,accumulator是一个二元操作符,通常用于对流中的元素进行聚合操作。
例如,求总身高
Optional<Integer> reduce = list.stream().map(Forlan::getHeight).reduce(Integer::sum);
4)collect
collect(Collector<? super T, A, R> collector):将流中的元素收集到一个集合中,可以是列表、集合或映射等,collector定义了收集的方式。
例如,将List变为Set
Set<Forlan> collect = list.stream().collect(Collectors.toSet());
5)min
min(Comparator<? super T> comparator):返回流中的最小值,comparator定义了元素的比较方式,返回一个Optional类型的值。
Optional<Forlan> min = list.stream().min(Comparator.comparing(Forlan::getHeight));
返回身高最矮的对象
Optional[Forlan{name='林黄', height=160, weight=110}]
6)max
max(Comparator<? super T> comparator):返回流中的最大值,comparator定义了元素的比较方式,返回一个Optional类型的值。
Optional<Forlan> max = list.stream().max(Comparator.comparing(Forlan::getHeight));
返回身高最高的对象
Optional[Forlan{name='王五', height=189, weight=160}]
7)count
count():返回流中的元素个数,返回一个long类型的值。
例如,统计元素个数
long count = list.stream().count();
8)anyMatch
anyMatch(Predicate<? super T> predicate):判断流中是否存在至少一个元素满足给定的条件,返回一个boolean类型的值
boolean flag = list.stream().anyMatch(p -> p.getHeight() > 180);
判断是否有人身高大于180,运行结果如下:
true
9)allMatch
allMatch(Predicate<? super T> predicate):判断流中的所有元素是否都满足给定的条件,返回一个boolean类型的值
boolean flag = list.stream().allMatch(p -> p.getHeight() > 160);
判断是否所有人身高大于160,运行结果如下:
false
10)noneMatch
noneMatch(Predicate<? super T> predicate):判断流中是否没有任何元素满足给定的条件,返回一个boolean类型的值
boolean flag = list.stream().noneMatch(p -> p.getHeight() > 190);
判断是否不存在身高大于190,运行结果如下:
false
11)findFirst
findFirst():返回流中的第一个元素,返回一个Optional类型的值。
Optional<Forlan> first = list.stream().findFirst();
12)findAny
findAny():返回流中的任意一个元素,返回一个Optional类型的值。
Optional<Forlan> any= list.stream().findAny();
三、综合使用
定义一个公共操作的List,如下:
List<Forlan> list = new ArrayList<Forlan>() {{
add(new Forlan("张三", 177, 121));
add(new Forlan("李四", 166, 140));
add(new Forlan("王五", 189, 160));
add(new Forlan("张大", 177, 150));
add(new Forlan("林黄", 160, 110));
}};
1、筛选出满足任意指定条件的Forlan对象
List<Predicate> ruleList = new ArrayList<>();
Predicate<Forlan> nameConditon = c -> c.getName().contains("张");
Predicate<Forlan> heightConditon = c -> c.getHeight() > 180;
Predicate<Forlan> weightConditon = c -> c.getWeight() < 120;
ruleList.add(nameConditon);
ruleList.add(heightConditon);
ruleList.add(weightConditon);
Predicate predicate = ruleList.stream().reduce((a, b) -> a.or(b)).get();
运行结果如下:
Forlan{name='张三', height=177, weight=121}
Forlan{name='王五', height=189, weight=160}
Forlan{name='张大', height=177, weight=150}
Forlan{name='林黄', height=160, weight=110}
2、将List转为Map对象
Map<String, Integer> nameHeightMap = list.stream().collect(Collectors.toMap(Forlan::getName, Forlan::getHeight, (k1, k2) -> k1));
Map<String, Forlan> nameMap = list.stream().collect(Collectors.toMap(Forlan::getName, Function.identity()));
3、按照身高分组并统计每组有多少个
Map<Integer, Long> groupedAndCounted = list.stream()
.collect(Collectors.groupingBy(Forlan::getHeight, Collectors.counting()));
四、总结
总的来说,就是通过stream把数据放到流水线上,然后执行各种中间加工,最终打包出库。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!