Java---stream流的使用
筛选、切片和映射
查找、匹配和归约
使用数值范围等数值流
从多个源创建流
无限流
筛选、切片和映射
谓词筛选:该操作会接受一个谓词(一个返回boolean 的函数)作为参数,并返回一个包括所有符合谓词的元素的流。
- Stream<T> filter(Predicate<? super T> predicate) 返回由与此给定谓词匹配的此流的元素组成的流(保留符合条件的元素)。
- Stream<T> distinct() 返回由该流的不同元素(根据
Object.equals(Object))组成的流。对于有序流,选择不同的元素是稳定的(对于重复的元素,首先在遇到顺序中出现的元素被保留。)对于无序流,不能保证稳定性。
流的切片:使用Stream 的一些操作结合谓词,你可以高效地选择或者丢弃流中的元素,譬如忽略流的前几个元素,或者按照设定的大小对流实施截短操作。
Java 9 引入了两个新方法,可以高效地选择流中的元素,这两个方法分别是:takeWhile和dropWhile。(多用于有序集合)
- default Stream<T> takeWhile(Predicate<? super T> predicate) 与filter类似,但是会在遭遇第一个不符合要求的元素时停止处理
- default Stream < T > dropWhile ( Predicate <? super T > predicate) 执行条件判断,直到满足条件停止判断,即dropWhile只执行一次
-
List<Integer> list = Lists.newArrayList(1,2,6,4,5,7); list.stream().dropWhile(o->o<5).forEach(System.out::println); //6 4 5 7
- Stream<T> limit(long maxSize) 该方法会返回另一个不超过给定长度的流
- Stream<T> skip(long n) 返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。
映射:一个非常常见的数据处理套路就是从某些对象中选择信息。比如在SQL 里,你可以从表中选择一列。Stream API 也通过map 和flatMap 方法提供了类似的工具。
它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素,映射是“创建一个新版本”而不是去“修改”。
List<String> dishNames = menu.stream().map(Dish::getName).collect(toList());
flatMap 方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。
List<String> uniqueCharacters =
words.stream()
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());
查找和匹配
boolean anyMatch(Predicate<? super T> predicate)
anyMatch 方法可以回答“流中是否有一个元素能匹配给定的谓词”,anyMatch 方法返回一个boolean,因此是一个终端操作。
boolean allMatch(Predicate<? super T> predicate)
查看流中的元素是否都能匹配给定的谓词
boolean noneMatch(Predicate<? super T> predicate)
和allMatch 相对的是noneMatch。它可以确保流中没有任何元素与给定的谓词匹配。
Optional<T> findAny()
findAny 方法将返回当前流中的任意元素
Optional<T> findFirst()
返回描述此流的第一个元素的Optional如果流为空,则返回一个空的Optional。 如果流没有遇到顺序,则可能会返回任何元素。
为什么会同时有findFirst 和findAny 呢?答案是并行。
找到第一个元素在并行上限制更多。如果你不关心返回的元素是哪个,请使用findAny,因为它在使用并行流时限制较少。
归约
T reduce(T identity,BinaryOperator<T> accumulator)
使用提供的identity和associative累积功能对此流的元素执行reduction ,并返回减小的值。 这相当于:
T result = identity; for (T element : this stream) result = accumulator.apply(result, element) return result;
总和,最小,最大,平均和字符串连接都是减少的特殊情况。 一个数字流可以表示为: Integer sum = integers.reduce(0, (a, b) -> a+b); 或者: Integer sum = integers.reduce(0, Integer::sum);
操 作 类 型 返回类型 使用的类型/函数式接口 函数描述符 filter 中间 Stream<T> Predicate<T> T -> boolean distinct 中间(有状态–无界) Stream<T> takeWhile 中间 Stream<T> Predicate<T> T -> boolean dropWhile 中间 Stream<T> Predicate<T> T -> boolean skip 中间(有状态–无界) Stream<T> long limit 中间(有状态–无界) Stream<T> long map 中间 Stream<R> Function<T, R> T -> R flatMap 中间 Stream<R> Function<T,Stream<R>> T -> Stream<R> sorted 中间(有状态–无界) Stream<T> Comparator<T> (T, T) -> int anyMatch 终端 boolean Predicate<T> T -> boolean noneMatch 终端 boolean Predicate<T> T -> boolean allMatch 终端 boolean Predicate<T> T -> boolean findAny 终端 Optional<T> findFirst 终端 Optional<T> forEach 终端 void Consumer<T> T -> void collect 终端 R Collector<T, A, R> reduce 终端(有状态–有界) Optional<T> BinaryOperator<T> (T, T) -> T count 终端 long
原始类型流特化 :Java 8 引入了三个原始类型特化流接口:IntStream、DoubleStream 和LongStream,分别将流中的元素特化为int、long 和double,从而避免了暗含的装箱成本。
这些特化的原因并不在于流的复杂性,而是装箱造成的复杂性——即类似int 和Integer 之间的效率差异。
映射到数值流
将流转换为特化版本的常用方法是mapToInt、mapToDouble 和mapToLong。
int calories = menu.stream() .mapToInt(Dish::getCalories) .sum();
转换回对象流
要把原始流转换成一般流(每个int 都会装箱成一个Integer),可以使用boxed 方法
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
数值范围
Java 8 引入了两个可以用于IntStream 和LongStream 的静态方法,帮助生成这种范围:(range,rangeClosed]。
IntStream evenNumbers = IntStream.rangeClosed(1, 100) .filter(n -> n % 2 == 0); System.out.println(evenNumbers.count());
构建流
由值创建流:使用静态方法Stream.of,通过显式值创建一个流。
Stream<String> stream = Stream.of("Modern ", "Java ", "In ", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
你可以使用empty 得到一个空流,如下所示:
Stream<String> emptyStream = Stream.empty();
由可空对象创建流:处理的对象有可能为空,而又需要把它们转换成流
Stream<String> homeValueStream = Stream.ofNullable(System.getProperty("home"));
由数组创建流:使用静态方法Arrays.stream 从数组创建一个流
int[] numbers = {2, 3, 5, 7, 11, 13}; int sum = Arrays.stream(numbers).sum();
由文件生成流:Java 中用于处理文件等I/O 操作的NIO API(非阻塞 I/O)已更新,以便利用Stream API。
long uniqueWords = 0; try(Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){ uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))) .distinct() .count(); } catch(IOException e){ }
由函数生成流:创建无限流
Stream API 提供了两个静态方法来从函数生成流:Stream.iterate 和Stream.generate。这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。
Stream.iterate(0, n -> n + 2) .limit(10) .forEach(System.out::println);
与iterate 方法类似,generate 方法也可让你按需生成一个无限流。但generate 不是依次对每个新生成的值应用函数的。它接受一个Supplier<T>类型的Lambda 提供新的值。
Stream.generate(Math::random) .limit(5) .forEach(System.out::println);
用流收集数据Collectors参考下一节
更多方法参考Java官方文档 https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html

浙公网安备 33010602011771号