Stream(流)是一种用于处理集合数据的抽象概念。它可以让我们以一种类似于流水线的方式对数据进行操作和处理。Stream API 是 Java 8 引入的一个功能强大的工具,它提供了一种简洁而高效的方式来处理集合数据。通过使用 Stream,我们可以对集合进行过滤、映射、排序、聚合等操作,而无需编写繁琐的循环和条件语句。Stream 的操作可以串行执行,也可以并行执行,从而提高处理大量数据的效率。
用于对流中的元素进行处理和转换,但并不会触发流的遍历和处理,会返回一个新的Stream流,可以继续进行其他的中间操作或终端操作。
通过组合不同的中间操作,可以构建出一个流水线,以实现复杂的数据处理逻辑。但需要注意的是,中间操作只有在遇到终端操作时才会被触发执行。
中间操作包括有状态和无状态操作两种:
用于触发流的遍历和处理,并产生最终的结果或副作用。在流的最后调用,执行终止操作后,流将无法再使用。
终止操作包括短路操作和非短路操作两种:
先获取流,然后可以执行各种中间操作,最终终止操作。
获取流的方式,官方文档内容如下:
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().
也就是可以从一个数据源,如集合、数组中获取流
可以使用集合的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();
可以使用Arrays.stream()方法来获取一个Stream对象,例如:
Integer[] array = {1, 2, 3};
Stream<Integer> stream = Arrays.stream(array);
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);
可以使用java.nio.file.Files类的lines()方法来获取一个Stream对象,读取文件中的行,例如:
Path path = Paths.get("G:\\output.txt");
Stream<String> stream = Files.lines(path);
以上是一些常用的获取方式
操作的中间环节,对数据源的数据进行操作,当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”
中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线
比如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));
}};
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}
map(Function
list.stream().map(Forlan::getName).forEach(System.out::println);
张三
李四
王五
张大
林黄
flatMap(Function
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
distinct():去除重复的元素。
list.stream().map(Forlan::getHeight).distinct().forEach(System.out::println);
177
166
189
160
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}
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
limit(long maxSize):限制Stream中元素的数量。
list.stream().limit(1).forEach(System.out::println);
限制返回一个元素,运行结果如下:
Forlan{name='张三', height=177, weight=121}
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}
执行操作,返回结果
当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终止操作
比如:forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator。
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}
toArray():将流中的元素转换为数组,返回一个包含流中所有元素的数组。
例如,將List转为数组
Forlan[] array = list.stream().toArray(Forlan[]::new);
reduce(T identity, BinaryOperator accumulator):将流中的元素按照指定的操作进行归约,返回一个值。identity是初始值,accumulator是一个二元操作符,通常用于对流中的元素进行聚合操作。
例如,求总身高
Optional<Integer> reduce = list.stream().map(Forlan::getHeight).reduce(Integer::sum);
collect(Collector super T, A, R> collector):将流中的元素收集到一个集合中,可以是列表、集合或映射等,collector定义了收集的方式。
例如,将List变为Set
Set<Forlan> collect = list.stream().collect(Collectors.toSet());
min(Comparator super T> comparator):返回流中的最小值,comparator定义了元素的比较方式,返回一个Optional类型的值。
Optional<Forlan> min = list.stream().min(Comparator.comparing(Forlan::getHeight));
返回身高最矮的对象
Optional[Forlan{name='林黄', height=160, weight=110}]
max(Comparator super T> comparator):返回流中的最大值,comparator定义了元素的比较方式,返回一个Optional类型的值。
Optional<Forlan> max = list.stream().max(Comparator.comparing(Forlan::getHeight));
返回身高最高的对象
Optional[Forlan{name='王五', height=189, weight=160}]
count():返回流中的元素个数,返回一个long类型的值。
例如,统计元素个数
long count = list.stream().count();
anyMatch(Predicate super T> predicate):判断流中是否存在至少一个元素满足给定的条件,返回一个boolean类型的值
boolean flag = list.stream().anyMatch(p -> p.getHeight() > 180);
判断是否有人身高大于180,运行结果如下:
true
allMatch(Predicate super T> predicate):判断流中的所有元素是否都满足给定的条件,返回一个boolean类型的值
boolean flag = list.stream().allMatch(p -> p.getHeight() > 160);
判断是否所有人身高大于160,运行结果如下:
false
noneMatch(Predicate super T> predicate):判断流中是否没有任何元素满足给定的条件,返回一个boolean类型的值
boolean flag = list.stream().noneMatch(p -> p.getHeight() > 190);
判断是否不存在身高大于190,运行结果如下:
false
findFirst():返回流中的第一个元素,返回一个Optional类型的值。
Optional<Forlan> first = list.stream().findFirst();
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));
}};
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}
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()));
Map<Integer, Long> groupedAndCounted = list.stream()
.collect(Collectors.groupingBy(Forlan::getHeight, Collectors.counting()));
总的来说,就是通过stream把数据放到流水线上,然后执行各种中间加工,最终打包出库。