Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
    4-11 Stream和Optional - 图1
    它看起来就像一个工厂的流水线一样!我们就可以把一个Stream当做流水线处理:

    1. public static void main(String[] args) {
    2. List<String> list = new ArrayList<>();
    3. list.add("A");
    4. list.add("B");
    5. list.add("C");
    6. //移除为B的元素
    7. Iterator<String> iterator = list.iterator();
    8. while (iterator.hasNext()){
    9. if(iterator.next().equals("B")) iterator.remove();
    10. }
    11. //Stream操作
    12. list = list //链式调用
    13. .stream() //获取流
    14. .filter(e -> !e.equals("B")) //只允许所有不是B的元素通过流水线
    15. .collect(Collectors.toList()); //将流水线中的元素重新收集起来,变回List
    16. System.out.println(list);
    17. }

    可能从上述例子中还不能感受到流处理带来的便捷,我们通过下面这个例子来感受一下:

    1. public static void main(String[] args) {
    2. List<Integer> list = new ArrayList<>();
    3. list.add(1);
    4. list.add(2);
    5. list.add(3);
    6. list.add(3);
    7. list = list
    8. .stream()
    9. .distinct() //去重(使用equals判断)
    10. .sorted((a, b) -> b - a) //进行倒序排列
    11. .map(e -> e+1) //每个元素都要执行+1操作
    12. .limit(2) //只放行前两个元素
    13. .collect(Collectors.toList());
    14. System.out.println(list);
    15. }

    当遇到大量的复杂操作时,我们就可以使用Stream来快速编写代码,这样不仅代码量大幅度减少,而且逻辑也更加清晰明了(如果你学习过SQL的话,你会发现它更像一个Sql语句)

    注意:不能认为每一步是直接依次执行的!

    1. List<Integer> list = new ArrayList<>();
    2. list.add(1);
    3. list.add(2);
    4. list.add(3);
    5. list.add(3);
    6. list = list
    7. .stream()
    8. .distinct() //断点
    9. .sorted((a, b) -> b - a)
    10. .map(e -> {
    11. System.out.println(">>> "+e); //断点
    12. return e+1;
    13. })
    14. .limit(2) //断点
    15. .collect(Collectors.toList());
    16. //实际上,stream会先记录每一步操作,而不是直接开始执行内容,当整个链式调用完成后,才会依次进行!

    接下来,我们用一堆随机数来进行更多流操作的演示:

    1. public static void main(String[] args) {
    2. Random random = new Random(); //Random是一个随机数工具类
    3. random
    4. .ints(-100, 100) //生成-100~100之间的,随机int型数字(本质上是一个IntStream)
    5. .limit(10) //只获取前10个数字(这是一个无限制的流,如果不加以限制,将会无限进行下去!)
    6. .filter(i -> i < 0) //只保留小于0的数字
    7. .sorted() //默认从小到大排序
    8. .forEach(System.out::println); //依次打印
    9. }

    我们可以生成一个统计实例来帮助我们快速进行统计:

    1. public static void main(String[] args) {
    2. Random random = new Random(); //Random是一个随机数工具类
    3. IntSummaryStatistics statistics = random
    4. .ints(0, 100)
    5. .limit(100)
    6. .summaryStatistics(); //获取语法统计实例
    7. System.out.println(statistics.getMax()); //快速获取最大值
    8. System.out.println(statistics.getCount()); //获取数量
    9. System.out.println(statistics.getAverage()); //获取平均值
    10. }

    普通的List只需要一个方法就可以直接转换到方便好用的IntStream了:

    1. public static void main(String[] args) {
    2. List<Integer> list = new ArrayList<>();
    3. list.add(1);
    4. list.add(1);
    5. list.add(2);
    6. list.add(3);
    7. list.add(4);
    8. list.stream()
    9. .mapToInt(i -> i) //将每一个元素映射为Integer类型(这里因为本来就是Integer)
    10. .summaryStatistics();
    11. }

    我们还可以通过flat来对整个流进行进一步细分:

    1. public static void main(String[] args) {
    2. List<String> list = new ArrayList<>();
    3. list.add("A,B");
    4. list.add("C,D");
    5. list.add("E,F"); //我们想让每一个元素通过,进行分割,变成独立的6个元素
    6. list = list
    7. .stream() //生成流
    8. .flatMap(e -> Arrays.stream(e.split(","))) //分割字符串并生成新的流
    9. .collect(Collectors.toList()); //汇成新的List
    10. System.out.println(list); //得到结果
    11. }

    我们也可以只通过Stream来完成所有数字的和,使用reduce方法:

    1. public static void main(String[] args) {
    2. List<Integer> list = new ArrayList<>();
    3. list.add(1);
    4. list.add(2);
    5. list.add(3);
    6. int sum = list
    7. .stream()
    8. .reduce((a, b) -> a + b) //计算规则为:a是上一次计算的值,b是当前要计算的参数,这里是求和
    9. .get(); //我们发现得到的是一个Optional类实例,不是我们返回的类型,通过get方法返回得到的值
    10. System.out.println(sum);
    11. }

    通过上面的例子,我们发现,Stream不喜欢直接给我们返回一个结果,而是通过Optinal的方式,那么什么是Optional呢?

    Optional类是Java8为了解决null值判断问题,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。总而言之,就是对控制的一个判断,为了避免空指针异常。

    1. public static void main(String[] args) {
    2. String str = null;
    3. if(str != null){ //当str不为空时添加元素到List中
    4. list.add(str);
    5. }
    6. }

    有了Optional之后,我们就可以这样写:

    1. public static void main(String[] args) {
    2. String str = null;
    3. Optional<String> optional = Optional.ofNullable(str); //转换为Optional
    4. optional.ifPresent(System.out::println); //当存在时再执行方法
    5. }

    就类似于Kotlin中的:

    1. var str : String? = null
    2. str?.upperCase()

    我们可以选择直接get或是当值为null时,获取备选值:

    public static void main(String[] args) {
        String str = null;
        Optional optional = Optional.ofNullable(str);   //转换为Optional(可空)
        System.out.println(optional.orElse("lbwnb"));
             // System.out.println(optional.get());   这样会直接报错
    }
    

    同样的,Optional也支持过滤操作和映射操作,不过是对于单对象而言:

    public static void main(String[] args) {
        String str = "A";
        Optional optional = Optional.ofNullable(str);   //转换为Optional(可空)
        System.out.println(optional.filter(s -> s.equals("B")).get());   //被过滤了,此时元素为null,获取时报错
    }
    
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        String str = "A";
        Optional optional = Optional.ofNullable(str);   //转换为Optional(可空)
        System.out.println(optional.map(s -> s + "A").get());   //在尾部追加一个A
    }
    

    其他操作自学了解。