Java 8 流的学习总结

1、首先谈谈什么是流?

流是从支持数据处理操作的源中生成的元素序列。

拆分理解这句话:

  • 元素序列:流提供了一个接口,可以访问特定元素类型的一组有序值;
  • :被处理的数据,从有序集合生成流时会保留原有的顺序;由列表生成的流,其元素顺序和列表一致;
  • 数据处理操作:与数据库操作类似,流操作可以顺序执行,也可以并行执行;
  • 流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一条大的流水线;
  • 内部迭代:与使用迭代器显示迭代的集合不同,流的迭代操作是在背后进行的;

流有什么好处?或者我们为什么要使用流?

        // 取出用户中大于24岁的人,并排序后输出
        List<User> list = Arrays.asList(
                new User("Dz", 20),
                new User("Ym", 18),
                new User("Json", 28),
                new User("Dion", 25),
                new User("Tom", 32),
                new User("Kitty", 27)
        );

        // 传统的 Java 编码 start:

        List<User> newList = new ArrayList<>();

        for (User aList : list) {
            if (aList.getAge() > 24) {
                newList.add(aList);
            }
        }

        Collections.sort(newList, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        });

        for (User item : newList) {
            System.out.println(item.getName() + ":" + item.getAge());
        }

        // 传统的 Java 编码 end

        // 流操作
        list.stream()   //list.parallelStream() 可以并发执行
                .filter(u -> u.getAge() > 24)
                .sorted(Comparator.comparing(User::getAge))
                .forEach(user -> System.out.println(user.getName() + ":" +user.getAge()));

流允许你以使用声明性的方式处理数据集合,而不是临时编写一个实现。具体好处如下(结合上述示例):

  • 声明性 - 更简洁、易读;
  • 可复合 - 更灵活;
  • 可并行 - 性能更好;

 

2、流与集合对比

  • 集合是一个内存中的数据结构,包含数据结构中目前所有的值;
  • 而流是概念上固定的数据结构,其元素是按需加载的;
  • 流只能被遍历一次,遍历完之后就被消费掉了;
  • 遍历数据的方式也是他们一个关键区别:
    • Collection 需要用户去编码迭代,这称为外部迭代;
    • Streams 库使用内部迭代,不需要去编码,它内部帮忙把这些事情做了;
  • 集合主要是访问数据,而流的目的在于数据计算

 

3、流操作

流主要两种类别的操作:中间操作终端操作

中间操作形成流水线、而终端操作则是从流水线中生成结果。

使用流则包含三个要素:

  • 数据源;
  • 中间操作链;
  • 终端操作;

 

4、使用流

4.1 筛选: (.filter 筛选比较用的比较多)

  • filter():该操作接受一个谓词,筛选出符合谓词的元素的流;
  • distinct():返回一个元素各异的流(根据流所生成元素的 hashCode 和 equals 方法实现);
  • limit(n):截短流,返回一个不会超过给定长度的流,若流是有序的,最多返回前 n 个原色;
  • skip(n):跳过元素,返回一个丢弃掉前 n 个元素的流,如果流中不足 n 个则返回一个空流;

4.2 映射: (map 将一个实体映射成一个新的元素,如获取一个实体的一项属性)

  • map():对流中每个一个元素应用函数,并将其映射成一个新的元素;
List<String> words = Arrays.asList("Java", "Lambda", "Work", "Action");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
  • flatMap(Arrays::stream):流的扁平化,把所有的流连接 起来成为一个流;
// 列 words 出里面各不相同的字符
// .map(word -> word.split("")) 返回的是 Stream<String[]>,不是我们想要的Stream<String>

String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);

words.stream()
.map(word -> word.split(""))
.map(Arrays::stream)
.distinct()
.collect(toList());


List<String> uniqueCharacters =
words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());

// 所有使用 map(Arrays::stream) 时生成的单个流都被合并起来,即扁平化为一个流

4.3 查找与匹配: (查找满足条件的一项)

Stream API 通过 allMatch、anyMatch、noneMatch、findFirst 和 findAny 方法提供了这样的工具。

Optional<Dish> dish =
menu.stream()
.filter(Dish::isVegetarian)
.findAny()
.ifPresent(d -> System.out.println(d.getName());    

// 介绍如下文所示

Optional简介

Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在。

  • isPresent()将在 Optional 包含值的时候返回 true, 否则返回 false;
  • ifPresent(Consumer<T> block)会在值存在的时候执行给定的代码块;
  • T get()会在值存在时返回值,否则抛出一个 NoSuchElement 异常;
  • T orElse(T other)会在值存在时返回值,否则返回一个默认值;

 

4.4 归约:(将流归约成一个值 .reduce())

  • 对流求和;
  • 流中最大值、最小值问题;
  • 求流中元素个数;

.reduce()

  • 一个初始值,这里是0;
  • 一个 BinaryOperator<T>来将两个元素结合起来产生一个新值,这里我们用的是 lambda (a, b) -> a + b;

无初始值:reduce 还有一个重载的变体,它不接受初始值,但是会返回一个 Optional 对象;

Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
//为什么它返回一个Optional<Integer>呢?考虑流中没有任何元素的情况。
//reduce操作无法返回其和,因为它没有初始值。


//当然你也可以利用 Optional 自带的几个方法:
int sum = numbers.stream().reduce((a, b) -> (a + b)).orElse(0);

 

5 数值流 -- 原始类型流特化

  • IntStream;
  • DoubleStream;
  • LongStream;

 

6、总结

  • Streams API 可以表达复杂的数据处理查询;
  • 你可以使用 filter、distinct、skip 和 limit 对流做筛选和切片;
  • 你可以使用 map 和 flatMap 提取或转换流中的元素;
  • 你可以使用 findFirst 和 findAny 方法查找流中的元素。你可以用 allMatch 、noneMatch 和 anyMatch 方法让流匹配给定的谓词;
  • 这些方法都利用了短路:找到结果就立即停止计算;没有必要处理整个流;
  • 你可以利用 reduce 方法将流中所有的元素迭代合并成一个结果,例如求和或查找最大、最小元素;
  • filter 和 map 等操作是无状态的,它们并不存储任何状态。reduce 等操作要存储状态才能计算出一个值。sorted 和 distinct 等操作也要存储状态,因为它们需要把流中的所
  • 有元素缓存起来才能返回一个新的流。这种操作称为有状态操作;
  • 流有三种基本的原始类型特化:IntStream、DoubleStream 和 LongStream。它们的操作也有相应的特化;
  • 流不仅可以从集合创建,也可从值、数组、文件以及 iterate 与 generate 等特定方法 创建;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章