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

posted @ 2021-07-09 16:35  如果可以就好了  阅读(383)  评论(0)    收藏  举报