Stream API

扫码查看

一、简介

1、stream 是Java API的新成员,他允许以\(\color{#DC143C}{声明性}\)方式处理数据\(\color{red}{集合}\),还可以\(\color{red}{透明}\)的并行处理
2、Stream API的好处

二、中间操作和终端操作

java.util.stream.Stream 中的Stream接口定义了许多操作,可以连接起来的流操作成为\(\color{red}{中间操作}\),关闭流的操作成为\(\color{red}{终端操作}\),分类只是基于流只能消费一次的事实

1、中间操作

常用中间操作:

filter中间StreamPredicateT -> booleanmap中间StreamFunction<T,R>T -> Rlimit中间Streamsorted中间StreamComparator( T, T) -> intdistinct中间Stream
2、终端操作

常用终端操作:

forEach终端消费流中的每个元素并对应其lambda,返回结果voidcount终端返回流中元素的个数,返回结果longcollect终端把流规约成一个集合

三、使用流

1、用谓词筛选(filter)

Streams接口支持filter方法,该操作会接受一个谓词作为参数,并返回一个包括所有符合谓词的元素的流。

List<Dish> list = menu.stram()
                .filter(Dish :: isVegetarian)
                .collect(toList());
2、去重(distinct)

流支持一个叫作distinct的方法,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流。

import java.util.Arrays;
import java.util.List;

public class Test {
    public static void main(String[] args) {

    List<Integer> numbers = Arrays.asList(1,2,1,3,3,2,4);
    numbers.stream()
            .filter(i -> i % 2 == 0)
            .distinct()
            .forEach(System.out :: println);
    }
}
3、截短流(limit)
        List<Dish> list = menu.stram()
                .filter(d -> d.getCalories() > 300)
                .limit(3)
                .collect(toList());
4、跳过元素(skip)
        List<Dish> list = menu.stram()
                .filter(d -> d.getCalories() > 300)
                .skip(2)
                .collect(toList());
5、映射(map)
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Test {

    public static void main(String[] args) {

        List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
        List<Integer> wordLengths = words.stream()
                .map(String::length)
                .collect(Collectors.toList());
                System.out.println(wordLengths.toString());
    }
}
# 结果:[6, 7, 2, 6]
6、流的扁平化(flatMap)
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Test {

    public static void main(String[] args) {

        List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
        List<String> uniqueCharacters = words.stream()
                .map(w -> w.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
                System.out.println(uniqueCharacters.toString());
    }
}
# 结果:[J, a, v,  , 8, L, m, b, d, s, I, n, A, c, t, i, o]
7、查找和匹配
  • 一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。Stream API通过allMatch、 anyMatch、 noneMatch、 findFirst和findAny方法提供了这样的工具,他们都是终端操作。
  • anyMatch、 allMatch和noneMatch这三个操作都用到了我们所谓的短路,这就是大家熟悉的Java中&&和||运算符短路在流中的版本。
  • 对于流而言,某些操作 (例如allMatch、anyMatch、noneMatch、findFirst和findAny)不用处理整个流就能得到结果。只要找到一个元素,就可以有结果了。同样, limit也是一个短路操作:它只需要创建一个给定大小的流,而用不着处理流中所有的元素。在碰到无限大小的流的时候,这种操作就有用了:它们可以把无限流变成有限流。

    7、 归约(reduce)
  • reduce函数可以将流中所有元素反复结合起来,得到一个值。这样的查询可以被归类为归约操作(将流归约成一个值)。用函数式编程语言的术语来说,这称为折叠(fold)。
  • reduce提供两个版本,一个是带初始值,一个不带初始值(返回Optional对象)。

    四、有状态和无状态

  • 诸如map或filter等操作会从输入流中获取每一个元素,并在输出流中得到0或1个结果。这些操作一般都是无状态的:它们没有内部状态。
  • 诸如reduce、 sum、 max等操作需要内部状态来累积结果,从流中操作都需要知道先前的历史,因此是有状态操作

filter中间StreamPredicateT -> booleandistinct中间(有状态-无界)Streamskip中间(有状态-有界)Streamlonglimit中间(有状态-有界)Streamlongmap中间StreamFunction<T,R>T -> RflatMap中间StreamFunction<T,Stream>T -> Streamsorted中间(有状态-无界)StreamComparator(T , T -> int)anyMatch终端booleanPredicateT -> booleannoneMatch终端booleanPredicateT -> booleanallMatch终端booleanPredicateT -> booleanfindAny终端OptionalfindFirst终端OptionalforEach终端voidConsumerT -> voidcollect终端RCollector<T,A,R>reduce终端(有状态-有界)OptionalBinaryOperator(T, T) -> Tcount终端long

五、收集器

● 预定义收集器

toListList把流中所有项目收集到一个ListList<Dish> dishs = menuStream.collect(toList());toSetSet把流中所有项目收集到一个Set,删除重复项Set<Dish> dishs = menuStream.collect(toSet());toSetSet把流中所有项目收集到一个Set,删除重复项Set<Dish> dishs = menuStream.collect(toSet());toCollectionCollection把流中所有项目收集到给定的供应源创建的集合Collection<Dish> dishs = menuStream.collect(toCollection,ArrayList :: new);countingLong计算流中元素的个数long howManyDishes = menuStream.collect(counting())summingIntInteger对流中的项目一个整数属性求和int totalCalories = menuStream.collect(summingInt(Dish :: getCalories));averagingIntDouble计算流中项目Integer属性的平均值double avgCalories = menuStream.collect(averagingInt(Dish :: getCalories));summarizingIntIntSummaryStatistics收集关于流中项目Integer属性的统计值,例如最大、最小、总和、平均值IntSummaryStatistics menuStatistics = menuStream.collect(summarizingInt(Dish :: getCalories));joiningString连接对流中每个项目toString方法所生成的字符串String shortMenue = menuStream.map(Dish :: getName).collect(joining(","));maxByOptional一个包裹了流中按照给定比较器选出的最大元素的Optional,或如果流为空则为Optional.empty()Optional<Dish> fattest = menuStream.collect(maxBy(comparingInt(Dish :: getCalories)));minByOptional一个包裹了流中按照给定比较器选出的最小元素的Optional,或如果流为空则为Optional.empty()Optional<Dish> lighttest = menuStream.collect(minBy(comparingInt(Dish :: getCalories)));reducing归类操作产生的类型从一个作为类加载器的初始值开始,利用BinaryOperator 与流中的元素逐个集合,从而将流规约为单个值int totalCalories = new menuStream.collect(reducing(0,Dish :: getCalories,Integer :: sum));collectingAndThen转换函数返回的类型包裹另一个收集器,对其结果应用转换函数int howManyDishes = new menuStream.collect(collectingAndThen (toList(),List :: size);collectingAndThen转换函数返回的类型包裹另一个收集器,对其结果应用转换函数int howManyDishes = new menuStream.collect(collectingAndThen (toList(),List :: size);groupingByMap<K,List>根据项目的一个属性的值对流中的项目做问组,并将属性值作为结果Map的键Map<Dish.Type,List<Dish>> dishesByType = menuStream.collect(groupingBy(Dish :: getType));partitioningByMap<Boolean,List>根据对流中每个项目应用谓词的结果来对项目进行分区Map<Boolean,List<Dish>> vegetarianDishes = menuStream.collect(partitioningBy(Dish :: isVegetarian));

本文作者:魂皓轩 欢迎关注公众号

本人保留所有权益,转载请注明出处。
欢迎有故事、有想法的朋友和我分享,可发送至 e-mail: lwqforit@163.com

12-25 22:53
查看更多