侧边栏壁纸
  • 累计撰写 57 篇文章
  • 累计创建 10 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

1、Java SE 8的流库

yilee
2023-04-04 / 0 评论 / 0 点赞 / 71 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-05-31,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

一、Java SE 8的流库

1.1 从迭代到流的操作

  1. 统计单词长度大于12的单词数量

    List<String> words = ...;
    // 方法一
    long count = 0;
    for (String w : words){
        if (w.length() > 12) count++;
    }
    // 方法二
    long count = words.stream()
    .filter(w -> w.length() > 12)
    .count();
    // 并行
    long count = words.parallelStream()
    .filter(w -> w.length() > 12)
    .count();
    // 流是用stream 或parallelStream 方法创建的。filter方法对其进行转换, 而count 方法是终止操作
    
  2. 流与集合

    1. 流并不存储其元素。这些元素可能存储在底层的集合中,或者是按需生成的。
    2. 流的操作不会修改其数据源。例如,filter 方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。
    3. 流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。例如,如果我们只想查找前5 个长单词而不是所有长单词,那么filter 方法就会在匹配到第5 个单词后停止过滤。因此,我们甚至可以操作无限流。
  3. 操作流时的典型流程

    1. 创建一个流。
    2. 指定将初始流转换为其他流的中间操作,可能包含多个步骤。
    3. 应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。从此之后,这流就再也不能用了。

1.2 流的创建

  1. 数组与流

    • 使用静态的Stream.of创建流

      String[] words = ...;
      Stream<String> words = Stream.of(words);
      // 或
      Stream<String> song = Stream.of("gently", "down", "the", "stream");
      
    • Array.stream(array, from, to) 可以从数组中位千from (包括)和to(不包括)的元素中创建一个流。

      String[] words = ...;
      Stream<String> silence = Array.stream(words, 0, words.length);
      
    • 创建不包含任何元素的流,静态的Stream.empty方法

      Stream<String> silence= Stream.empty();
      
    • 创建无限流

      • generate 方法会接受一个不包含任何引元的函数(或者从技术上讲,是一个 Supplier<T> 接口的对象) 。无论何时, 只要需要一个流类型的值,该函数就会被调用以产生一个这样的值。

        // 常量值的流
        Stream<String> echos = Stream.generate(() -> "Echo");
        // 获取一个随机流
        Stream<Double> randoms = Stream.generate(Math::random);
        // 无限序列
        
      • iterate 方法,它会接受一个“种子”值,以及一个函数(从技术上讲,是一个UnaryOperation<T> ),并且会反复地将该函数应用到之前的结果上。下方例子中,该序列中的第一个元素是种子 BigInteger.ZERO , 第二个元素是f(seed) ,即l (作为大整数),下一个元素是f(f (seed) ) , 即2, 后续以此类推。

        Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
        

        正则表达式: \\PL+ 表示非字母

      • Pattern 类有一个 splitAsStream方法,它会按照某个正则表达式来分割一个CharSequence 对象。

        Stream<String> words = Pattern.compile("\\PL+").splitAsStream(contents);
        
      • 静态的Files.lines 方法会返回一个包含了文件中所有行的Stream

        try (Stream<String> lines= Files.lines(path)) {
        	// Process lines
        }
        

1.3 filter 、map 和flatMap 方法

  1. **filter **: filter 转换会产生一个流,它的元素与某种条件相匹配。

    List<String> words = ...;
    long count = words.parallelStream().filter(w -> w.length() > 12).count();
    

    filter 的引元是Predicate(断言接口,对一个对象或是一个基本数据作出判断,要么判断为 true ,要么判断为 false) ,即从T 到boolean 的函数。

  2. map:map会按照某种方式来转换流中的值。在使用map 时,会有一个函数应用到每个元素上,并且其结果是包含了应用该函数后所产生的所有结果的流。

    Stream<String> lowercaseWords = words.stream().map(String::toLowerCase);
    // 或
    Stream<String> lowercaseWords = words.stream().map(s -> s.substring(0, 1))
    
  3. **flatMap **:假设我们有一个泛型G (例如Stream) ,以及将某种类型T 转换为 G<U> 的函数f 和将类型U 转换为 G<V> 的函数g 。然后, 我们可以通过使用flatMap 来组合它们,即首先应用f , 然后应用g 。这是单子论的关键概念。

    public static Stream<String> letters(String s) {
        List<String> result = new ArrayList<>();
        for (int i = 0; i < s.length(); i++) {
            result.add(s.substring(i, i + 1));
        }
        return result.stream();
    }
    // 在一个字符串流上映射letters,得到一个包含流的流,像[ ...["y","o"],["a", "t”]...] 。
    Stream<Stream<String>> result = words.stream().map(w -> letters(w));
    // 为了将其摊平为字母流[...“y", "o", " a", "t”...] ,可以使用flatMap 方法
    Stream<String> result = words.stream().flatMap(w -> letters(w));
    

1.4 抽取子流和连接流

  1. 抽取子流

    • 调用stream.limit(n) 会返回一个新的流,它在n 个元素之后结束(如果原来的流更短,那么就会在流结束时结束)。

      Stream<Double> randoms = Stream.generate(Math::random).limit(100);
      // 产生一个包含100 个随机数的流。
      
    • 调用stream . skip(n) 正好相反: 它会丢弃前n 个元素。

      // 第一个元素是没什么用的空字符串,因此跳过
      Stream<String> words = Stream.of(contents.split("\\PL+")).skip(1);
      
    • Stream 类的静态的concat 方法将两个流连接起来

      Stream<String> combined = Stream.concat(letters("Hello"), letters("World"));
      // the stream is ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]
      

1.5 其他的流转换

  1. distinct 方法会返回一个流,它的元素是从原有流中产生的,即原来的元素按照同样的顺序剔除重复元素后产生的。这个流显然能够记住它已经看到过的元素。

    Stream<String> uniqueWords = Stream.of("merrily", "merrily", "merrily", "gently").distinct();
    
  2. 对于流的排序,有多种sorted 方法的变体可用。其中一种用千操作Comparable 元素的流,而另一种可以接受一个Comparator 。

    // 对字符串排序,使得最长的字符串排在最前面
    Stream<String> longestFirst = words.stream().sorted(Comparator.comparing(String::length).reversed());
    
  3. peek 方法会产生另一个流,它的元素与原来流中的元素相同,但是在每次获取一个元素时,都会调用一个函数。

    // 当实际访问一个元素时,就会打印出来一条消息,主要用于调试
    Object[] powers = Stream.iterate(1.0, p -> p * 2).peek(e -> System.out.println("Fetching" + e)).limit(20).toArray();
    

1.6 简单约简

  • 约简是一种终结操作( terminaloperation) ,它们会将流约简为可以在程序中使用的非流值。
  1. 简单约简方法

    • count 方法会返回流中元素的数量。

    • max 和min,它们会返回最大值和最小值。

      // 获得流中的最大值
      Optional<String> largest = words.max(String::compareToIgnoreCase);
      System.out.println("largest: " + largest.orElse(""));
      
    • findFirst 返回的是非空集合中的第一个值。它通常会在与f i lter 组合使用时显得很有用。

      //找到第一个以字母Q 开头的单词
      Optional<String> startsWithQ = words.filter(s -> s.startsWith("Q")).findFirst();
      

      方法返回的是一个类型Optional 的值,它要么在其中包装了答案,要么表示没有任何值(因为流碰巧为空)

    • findAny 返回的是非空集合中的任意的匹配值。

      // 找到第任意一个以字母Q 开头的单词
      Optional<String> startsWithQ = words.parallel().filter(s -> s.startsWith("Q")).findAny();
      
    • anyMatch 是否存在匹配

      // 查询是否存在一个以字母Q 开头的单词
      boolean aWordStartsWithQ = words.parallel().anyMatch(s -> s.startsWith("Q"));
      
    • allMatch 和noneMatch 方法,它们分别会在所有元素和没有任何元素匹配断言的情况下返回true

1. 7 Optional 类型

  • Optional<T> 对象是一种包装器对象,要么包装了类型T 的对象,要么没有包装任何对象。对于第一种情况,我们称这种值为存在的。Optional<T> 类型被当作一种更安全的方式,用来替代类型T 的引用,这种引用要么引用某个对象,要么为null 。
  1. 如何使用Optional 值

    • 在不存在任何值的情况下产生相应的替代物。

      // optionalString is a Optional
      // 使用某种默认值
      String result = optionalString.orElse("");
      // 调用代码来计算默认值
      String result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName());
      // 没有任何值时抛出异常
      String result = optionalString.orElseThrow(IllegalStateException::new);
      
    • if Present 方法会接受一个函数。如果该可选值存在,那么它会被传递给该函数。否则,不会发生任何事情。函数不会返回任何值

      // EG: optionalValue.ifPresent(v -> Process v);
      optionalValue.ifPresent(v -> results.add(v));
      // 或
      optionalValue.ifPresent(results::add);
      
  2. 不适合使用Optional 值的方式

    • 如果没有正确地使用Optional 值,那么相比较以往的得到“某物或null " 的方式,你并没有得到任何好处。

    • get 方法会在Optional 值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个NoSuchElementException 对象。

      Optional<T> optionalValue = ...;
      optionalValue.get().someMethod();
      // 并不比下面的方式更安全
      T value = ...;
      value.someMethod();
      
  3. 创建Optional 值

    • Optional.of(value) 以value 创建一个Optional
    • Optional.empty( )创建一个空的Optional
    • ofNullable 方法被用来作为可能出现的null 值和可选值之间的桥梁。Optional.ofNullable(obj) 会在obj 不为null 的情况下返回 Optional.of(obj) ,否则会返回 Optional.empty( )。
  4. 用flatMap 来构建Optional 值的函数

    • 假设你有一个可以产生 Optional<T> 对象的方法f , 并且目标类型T 具有一个可以产生 Optional<U> 对象的方法g 。如果它们都是普通的方法,那么你可以通过调用s.f().g()来将它们组合起来。但是这种组合没法工作,因为s.f() 的类型为 Optional<T> ,而不是T 。这个工作可以由flatMap完成

      Optional<U> result = s.f().flatMap(T::g);
      // 如果s . f() 的值存在,那么g 就可以应用到它上面。否则,就会返回一个空Optional<U>
      

      如果有更多的可以产生Optional 值的方法或Lambda 表达式,那么就可以重复此过程。你可以直接将对fl atMap 的调用链接起来,从而构建由这些步骤构成的管道,只有所有步骤都成功时,该管道才会成功。

1.8 收集结果

  • 当处理完流之后,通常会想要查看其元素。

    • 调用 iterator 方法,它会产生可以用来访问元素的旧式风格的迭代器。

    • 调用forEach 方法,将某个函数应用于每个元素

    • 调用forEachOrdered 方法,按照流中的顺序来处理它们。

    • 将结果收集到数据结构。调用toArray,获得由流的元素构成的数组。由于stream.toArray( ) 会返回一个Object[]数组。,因此可以传入对应的数组构造器即可获取对应类型的数组

      String[] result= stream.toArray(String[]::new);
      
    • 将流中的元素收集到另一个目标中,有一个便捷方法collect 可用,它接受一个Collector 接口的实例。Collectors 类提供了大量用于生成公共收集器的工厂方法。

      // 将流收集到列表或集
      List<String> result = stream.collect(Collectors.toList());
      Set<String> result = stream.collect(Collectors.toSet()) ;
      // 控制获得的集的种类
      TreeSet<String>result=stream.collect(Collectors.toCollection(TreeSet::new));
      //通过连接操作来收集流中的所有字符串
      String result = stream.collect(Collectors.joining());
      //在元素之间增加分隔符
      String result = stream.collect(Collectors.joining(","));
      
    • 流中包含除字符串以外的其他对象,那么我们需要现将其转换为字符串

      String result = stream.map(Object::toString).collect(Collectors.joining(","));
      
    • 将流的结果约简为总和、平均值、最大值或最小值,可以使用summarizing(IntlLonglDouble) 方法中的某一个。这些方法会接受一个将流对象映射为数据的函数,同时,这些方法会产生类型为(IntlLonglDouble)SummaryStatistics 的结果, 同时计算总和、数量、平均值、最小值和最大值。

      Optional<String> optionalString = Optional.of("merrily");
      IntSummaryStatistics summary = stream.collect(Collectors.summarizingInt(String::length));
      double averageWordLength = summary.getAverage();
      double maxWordLength = summary.getMax();
      

1.9 收集到映射表中

  1. Collectors.toMap 方法有两个函数引元,分别用来产生映射表的键和值。

    Map<Integer, String> idToName = people.collect(Collectors.toMap(Person::getid, Person::getName));
    // 在通常情况下,值应该是实际的元素,因此第二个函数可以使用Function . identity( ) 。
    Map<Integer, String> idToName = people.collect(Collectors.toMap(Person::getid, Function.identity()));
    如果有多个元素具有相同的键,那么就会存在冲突,收集器将会抛出一个IllegalStateException对象。
    
    • 如果有多个元素具有相同的键,那么就会存在冲突,收集器将会抛出一个IllegalStateException对象。可以通过提供第3 个函数引元来覆盖这种行为,该函数会针对给定的已有值和新值来解决冲突并确定键对应的值。这个函数应该返回巳有值、新值或它们的组合。

      // 所有可用Locale 中的每种语言,不关心同一种语言是否可能会出现2 次
      Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
      Map<String, String> languageNames = locales.collect(
              Collectors.toMap(
                      Locale::getDisplayLanguage,
                      l -> l.getDisplayLanguage(l),
                      (existingValue, newValue) -> existingValue));
      
    • 若计算给定国家的所有语言,则需要获得 Map<String, Set<String>> 映射

      // 给定国家的所有语言
      Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
      Map<String, Set<String>> languageNames = locales.collect(
              Collectors.toMap(
                      Locale::getDisplayCountry,
                      l -> Collections.singleton(l.getDisplayLanguage(l)),
                      (existingValue, newValue) -> {
                          Set<String> union = new HashSet<>(existingValue);
                          union.addAll(newValue);
                          return union;
                      }));
      
    • 如果想要得到TreeMap, 那么可以将构造器作为第4 个引元来提供。你必须提供一种合并函数。

      Map<Integer, Person> idToPerson = people.co11ect(
              Collectors.toMap(
                      Person::getld,
                      Function.identity(),
                      (existingValue, newValue) -> { throw new IllegalStateException(); },
                      TreeMap::new));
      

      对于每一个toMap方法,都有一个等价的可以产生并发映射表的toConcurrentMap方法。单个并发映射表可以用于并行集合处理。当使用并行流时,共享的映射表比合并映射表要更高效。注意,元素不再是按照流中的顺序收集的,但是通常这不会有什么问题。

1.10 群组和分区

  1. 将具有相同特性的值群聚成组是非常常见的,并且groupingBy 方法直接就支持它。

    // 给定国家代码对应的所有地点
    Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
    Map<String, List<Locale>> countryToLocales = locales.collect(
            Collectors.groupingBy(Locale::getCountry));
    

    每个Locale 都有一个语言代码(例如英语的en) 和一个国家代码(例如美国的US ) 。Loeale en_US 描述的是美国英语,而en_IE 是爱尔兰英语。某些国家有多个Locale 。例如, ga_IE 是爱尔兰的盖尔语,而前面的示例也展示了我的JVM 知道瑞士有三个Locale 。

  2. 当分类函数是断言函数( 即返回boolean 值的函数)时,流的元素可以分区为两个列表:该函数返回true 的元素和其他的元素。在这种情况下,使用partitioningBy 比使用groupingBy 要更高效。

    // 将所有Locale 分成了使用英语和使用所有其他语言的两类
    Map<Boolean, List<Locale>> englishAndOtherLocales = locales.collect(
            Collectors.partitioningBy(l -> l.getLanguage().equals("en")));
    List<Locale> englishLocales = englishAndOtherLocales.get(true);
    

1.11 下游收集器

  1. groupingBy 方法会产生一个映射表,它的每个值都是一个列表。如果想要以某种方式来处理这些列表,就需要提供一个“下游收集器” 。如果想要获得集而不是列表,那么可以使用上一节中看到的Collector . toSet 收集器

    Map<String, Set<Locale>> countryToLocaleSet = locales.collect(
           Collectors.groupingBy(Locale::getCountry, Collectors.toSet()));
    
  2. counting 会产生收集到的元素的个数。

    // 对每个国家有多少个Local e 进行计数
    Map<String, Long> countryToLocaleCounts = locales.collect(
        Collectors.groupingBy(Locale::getCountry, Collectors.counting()));
    
  3. summing(Int I Long I Double) 会接受一个函数作为引元,将该函数应用到下游元素中,并产生它们的和。

    // 计算城市流中每个州的人口总和
    Map<String, Integer> countryToLocaleCounts = cities.collect(
            Collectors.groupingBy(City::getState, Collectors.summingInt(City::getPopulation)));
    
  4. maxBy 和minBy 会接受一个比较器,并产生下游元素中的最大值和最小值。

    // 产生每个州中最大的城市
    Map<String, OptionalCity<City>> countryToLocaleCounts = cities.collect(
            Collectors.groupingBy(City::getState, Collectors.maxBy(Comparator.comparing(City::getPopulation))))
    
  5. mapping 方法会产生将函数应用到下游结果上的收集器,并将函数值传递给另一个收集器。

    // 按照州将城市群组在一起。在每个州内部,我们生成了各个城市的名字,并按照最大长度约简。
    Map<String,Optional<String>> stateToLongestCityName = cities.collect(
            Collectors.groupingBy(City::getState,
                    Collectors.mapping(City::getName,
                            Collectors.maxBy(Comparator.comparing(String::length)))));
    // 把某国所有的语言收集到一个集中
    Map<String, Set<String>> countryToLanguages = locales.collect(
            Collectors.groupingBy(Locale::getDisplayCountry,
                    Collectors.mapping(Locale::getDisplayLanguage,
                            Collectors.toSet())));
    
  6. 如果群组和映射函数的返回值为int 、long 或d ouble , 那么可以将元素收集到汇总统计对象中,

    Map<String, Integer> countryToLocaleCounts = cities.collect(
            Collectors.groupingBy(City::getState,
                    Collectors.summarizingInt(City::getPopulation)));
    // 可以从每个组的汇总统计对象中获取这些函数值的总和、个数、平均值、最小值和最大值。
    
  7. 还有3 个版本的reducing 方法,它们都应用了通用的约简操作,下一节讲解

1.12 约简操作

  1. reduce 方法是一种用千从流中计算某个值的通用机制,其最简单的形式将接受一个二元函数,并从前两个元素开始待续应用它。

    // reduce 方法会计算v0+V1+V2+...,其中vi是流中的元素。如果流为空,那么该方法会返回一个Optional , 因为没有任何有效的结果。
    List<Integer> values = ...;
    Optional<Integer> sum = values.stream().reduce(Integer::sum);
    

    通常,如果reduce 方法有一项约简操作 op , 那么该约简就会产生v 。op v1 op v2 op …,其中我们将函数调用op(v1, V1+1 ) 写作V1 op V1+1 。这项操作应该是可结合的: 即组合元素时使用的顺序不应该成为问题。如求和、乘积、字符串连接、取最大值和最小值、求集的并与交等

  2. 第2 种形式

    // 如果流为空, 则会返回0,即不用处理Optional类
    List<Integer> values = ...;
    Optional<Integer> sum = values.stream().reduce(0, Integer::sum);
    
  3. 首先提供一种“累积器”函数(total , word)-> total + word.length( ),这个函数会被反复调用,产生累积的总和。其次,由于当计算被并行化时,会有多个这种类型的计算,你需要将它们的结果合并,因此,你需要提供第二个函数来执行此处理。当然,为了不处理Optional类,这里提供了一个默认值

    int result = words.reduce(0,
    (total, word) -> total + word.length(),
    (total1, total2) -> total1 + total2);
    

    通常,映射为数宇流并使用其方法来计算总和、最大值和最小值会更容易,而不是使用 reduce

    列如上述操作等价于: words . mapTolnt (String : : length). sum()

  • 注意

    有时reduce 会显得并不够通用。例如,假设我们想要收集BitSet 中的结果。如果收集操作是并行的,那么就不能直接将元素放到单个BitSet 中,因为BitSet 对象不是线程安全的。因此,我们不能使用reduce , 因为每个部分都需要以其自己的空集开始,并且reduce 只能让我们提供一个么元值。此时,应该使用collect, 它会接受单个引元:

    1. 一个提供者,它会创建目标类型的新实例,例如散列集的构造器。

    2. 一个累积器,它会将一个元素添加到一个实例上,例如add 方法。

    3. 一个组合器,它会将两个实例合并成一个,例如addAll 。

    4. 下面的代码展示了collect 方法是如何操作位集的:

      BitSet result = stream.collect(BitSet::new, BitSet::set, BitSet::or);
      

1.13 基本类型流

  1. 流库中具有专门的类型Int Stream 、LongStream 和DoubleStream , 用来直接存储基本类型值,而无需使用包装器。如果想要存储short 、char 、byte 和boolean , 可以使用IntStream , 而对于float , 可以使用DoubleStream 。为了创建IntStream , 需要调用IntStream.of 和Arrays.stream 方法:

    IntStream stream = IntStream.of(1, 1, 2, 3, 5);
    stream = Arrays.stream(values, from, to); // values is an int[] array
    // 与对象流一样,我们还可以使用静态的generate 和iterate 方法
    
  2. IntStream和LongStream 有静态方法range 和rangeClosed , 可以生成步长为l 的整数范围

    IntStream zeroToNinetyNine = IntStream.range(0, 100);// Upper bound is excluded
    IntStream zeroToHundred = IntStream.rangeClosed(0, 100);//  Upper bound is include
    
  3. CharSequence 接口拥有codePoints 和chars 方法,可以生成由字符的Unicode 码或由UTF-16 编码机制的码元构成的IntStream 。

    IntStream codes = sentence.codePoints();
    
  4. 当你有一个对象流时,可以用mapToint 、mapTolong 和mapToDouble 将其转换为基本类型流。

    Strea111<String> words = ...;
    IntStream lengths= words.mapTo!nt(String::length);
    
  5. 为了将基本类型流转换为对象流,需要使用boxed 方法

    Stream<Integer> integers = IntStream.range (0, 100).boxed();
    
  6. 基本类型流上的方法与对象流上的方法类似。其差异如下

    • toArray 方法会返回基本类型数组。

    • 产生可选结果的方法会返回一个Optional Int 、Optional Long 或 OptionalDouble 。这些类与Optional 类类似,但是具有getAslnt 、getAslong 和getAsDouble 方法,而不是get 方法。

    • 具有返回总和、平均值、最大值和最小值的sum 、average 、max 和min 方法。对象流没有定义这些方法。

    • summaryStatistics 方法会产生一个类型为IntSummaryStatistics 、LongSummaryStatistics或DoubleSummaryStatistics 的对象,它们可以同时报告流的总和、平均值、最大值和最小值。

      Random 类具有ints 、longs 和doubles 方法,它们会返回由随机数构成的基本类型流。

1.14 并行流

  1. 流使得并行处理块操作变得很容易。这个过程几乎是自动的,但是需要遵守一些规则。首先,必须有一个并行流。可以用Collec t ion.parallelStream( ) 方法从任何集合中获取一个并行流:

    Stream<String> parallelWords = words.parallelStream();
    

    parallel 方法可以将任意的顺序流转换为并行流。只要在终结方法执行时,流处于并行模式,那么所有的中间流操作都将被并行化。

    Stream<String> parallelWords = Stream.of(wordArray).parallel();
    
  2. 对字符串流中的所有短单词计数

    // 
    Map<Integer, Long> shortwordCounts = words
            .parallelStream()
            .filter(s -> s.length() < 10)
            .collect(Collectors.groupingBy(String::length, Collectors.counting()));
    

    警告:

    传递给并行流操作的函数不应该被堵塞。并行流使用fork-join 池来操作流的各个部分。如果多个流操作被阻塞,那么池可能就无法做任何事情了。

  3. 流顺序

    1. 从有序集合(数组和列表)、范围、生成器和迭代产生的流,或者通过调用Stream . sorted 产生的流,都是有序的。

    2. 排序并不排斥高效的并行处理。如,当计算stream.map(fun) 时, 流可以被划分为n 的部分,它们会被并行地处理。

    3. 当放弃排序需求时,有些操作可以被更有效地并行化。通过在流上调用unordered 方法,就可以明确表示我们对排序不感兴趣。

    4. 还可以通过放弃排序要求来提高limit 方法的速度。

      // 如果只想从流中取出任意n 个元素,而并不在意到底要获取哪些
      Stream<String> sample = words.parallelStream().unordered().limt(n) ;
      
  4. 合并映射表的代价很高昂。正是因为这个原因, Collectors.groupByConcurrent 方法使用了共享的并发映射表。为了从并行化中获益,映射表中值的顺序不会与流中的顺序相同。

    Map<Integer, List<String>> result = words.para11e1Stream().collect(
        Collectors.groupingByConcurrent(String::length));
    
    

    不要修改在执行某项流操作后会将元素返回到流中的集合(即使这种修改是线程安全的) 。记住,流并不会收集它们的数据,数据总是在单独的集合中。如果修改了这样的集合,那么流操作的结果就是未定义的。更准确地讲,因为中间的流操作都是惰性的,所以直到执行终结操作时才对集合进行修改仍旧是可行的。

  5. 为了让并行流正常工作,需要满足大量的条件:

    • 数据应该在内存中。必须等到数据到达是非常低效的。
    • 流应该可以被高效地分成若干个子部分。由数组或平衡二叉树支撑的流都可以工作得很好,但是Stream.iterate 返回的结果不行。
    • 流操作的工作量应该具有较大的规模。如果总工作负载并不是很大,那么搭建并行计算时所付出的代价就没有什么意义。
    • 流操作不应该被阻塞。
  6. 不要将所有的流都转换为并行流。只有在对已经位千内存中的数据执行大量计算操作时,才应该使用并行流。

API注释

1.1 从迭代到流的操作

java.util.stream.Stream 8

Stream<T> filter(Predicate< ? super T> p)
// 产生一个流, 其中包含当前流中满足P 的所有元素。
long count()
// 产生当前流中元素的数量。这是一个终止操作。

java.util.Collection 1.2

default Stream<E> stream()
default Stream<E> parallelStream()
// 产生当前集合中所有元素的顺序流或并行流。

1.2 流的创建

java.util.stream.Stream 8

static <T> Stream<T> of(T... values)
// 产生一个元素为给定值的流。
static <T> Stream<T> empty()
// 产生一个不包含任何元素的流。
static <T> Stream<T> generate(Supplier<T> s)
// 产生一个无限流,它的值是通过反复调用函数s 而构建的。
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
// 产生一个无限流,它的元素包含种子、在种子上调用f 产生的值、在前一个元素上调用f 产生的值,等等。

java.util.Arrays 1.2

static <T> Stream<T> stream(T[] array, int startlnclusive, int endExclusive) 8
// 产生一个流,它的元素是由数组中指定范围内的元素构成的。

java.util.regex.Pattern 1.2

Stream<String> splitAsStream(CharSequence input) 8
// 产生一个流,它的元素是输入中由该模式界定的部分。

java.nio.file.Files 7

static Stream<String> lines(Path path) 8
static Stream<String> lines(Path path, Charset cs) 8
// 产生一个流,它的元素是指定文件中的行,该文件的字符集为UTF- 8, 或者为指定的字符集。

java.util.function.Supplier 8

T get()
// 提供一个值。

1.3 filter 、map 和flatMap 方法

java.util.stream.Stream 8

Stream<T> filter(Predicate< ? super T> predicate)
// 产生一个流,它包含当前流中所有满足断言条件的元素。
<R> Stream<R> map(Funetion< ? super T, ? extends R> mapper)
// 产生一个流,它包含将mapper 应用千当前流中所有元素所产生的结果。
<R> Stream<R> flatMap(Function< ? super T, ? extends Stream< ? extends R>> mapper)
// 产生一个流,它是通过将mapper 应用千当前流中所有元素所产生的结果连接到一起而获得的。(注意,这里的每个结果都是一个流。)

1.4 抽取子流和连接流

java.util.stream.Stream 8

Stream<T> limit(long maxSize)
// 产生一个流,其中包含了当前流中最初的maxSize 个元素。
Stream<T> skip(long n)
// 产生一个流,它的元素是当前流中除了前n 个元素之外的所有元素。
static <T> Stream<T> concat(Stream< ? extends T> a, Stream< ? extends T> b)
// 产生一个流, 它的元素是a 的元素后面跟着b 的元素。

1.5 其他的流转换

java.util.stream.Stream 8

Stream<T> distinct()
// 产生一个流,包含当前流中所有不同的元素。
Stream<T> sorted()
Stream<T> sorted(Comparator< ? super T> comparator)
// 产生一个流,它的元素是当前流中的所有元素按照顺序排列的。第一个方法要求元素是实现了Comparable 的类的实例。
Stream<T> peek(Consumer< ? super T> action)
// 产生一个流,它与当前流中的元素相同,在获取其中每个元素时,会将其传递给action 。

1.6 简单约简

java.util.stream.Stream 8

Optional <T> max(Comparator< ? super T> comparator)
Optional <T> min(Comparator< ? super T> comparator)
// 分别产生这个流的最大元素和最小元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的Optional 对象。这些操作都是终结操作。
Optional<T> findFirst()
Optional <T> findAny()
// 分别产生这个流的第一个和任意一个元素,如果这个流为空,会产生一个空的
Optional 对象。这些操作都是终结操作。
boolean anyMatch(Predicate< ? super T> predicate)
boolean allMatch(Predicate< ? super T> predicate)
boolean noneMatch(Predicate< ? super T> predicate)
// 分别在这个流中任意元素、所有元素和没有任何元素匹配给定断言时返回true 。这些操作都是终结操作。

1.7.1 如何使用Optional 值

java.util.Optional 8

T orEl se(T other)
// 产生这个Optional 的值,或者在该Optional 为空时,产生other 。
T orElseGet(Supplier< ? extends T> other)
// 产生这个Optional 的值,或者在该Optional 为空时, 产生调用other 的结果。
<X extends Throwable> T orElseThrow(Supplier< ? extends X> exceptionSupplier)
// 产生这个Optional 的值,或者在该Optional 为空时,抛出调用exceptionSupplier的结果。
void ifPresent(Consumer< ? super T> consumer)
// 如果该Optional 不为空,那么就将它的值传递给consumer 。
<U> Optional <U> map(Funetion< ? super T, ? extends U> mapper)
// 产生将该Optional 的值传递给mapper 后的结果,只要这个Optional 不为空且结果不为null , 否则产生一个空Optional 。

1.7.2 不适合使用Optional 值的方式

java.util.Optional 8

T get()
// 产生这个Opt i onal 的值,或者在该Opt i onal 为空时,抛出一个NoSuchElementException对象。

1.7.3 创建Optional 值

java.util.Optional 8

static <T> Optional<T> of(T value)
static <T> Optional<T> ofNullable(T value)
// 产生一个具有给定值的Optional 。如果value 为null , 那么第一个方法会抛出一个NullPointerException 对象,而第二个方法会产生一个空Optional 。
static <T> Optional<T> empty()
// 产生一个空Optional 。

1.7.4 用flatMap 来构建Optional 值的函数

java.util.Optional 8

<U> Optional<U> flatMap(Function< ? super T, Optional<U>> mapper)
// 产生将mapper 应用于当前的Optional 值所产生的结果,或者在当前Optional 为空时,返回一个空Optional 。

1.8 收集结果

java.util.stream.BaseStream 8

Iterator<T> iterator()
// 产生一个用于获取当前流中各个元素的迭代器。这是一种终结操作。

java.util.stream.Stream 8

void forEach(Consumer< ? super T> action)
// 在流的每个元素上调用action 。这是一种终结操作。
Object[] toArray()
<A> A[] toArray(IntFunction<A[]> generator)
// 产生一个对象数组,或者在将引用A[]::new 传递给构造器时,返回一个A 类型的数组。这些操作都是终结操作。
<R,A> R collect(Collector< ? super T,A,R> collector)
// 使用给定的收集器来收集当前流中的元素。Collectors 类有用千多种收集器的工厂方法。

java.util.stream.Collectors 8

static <T> Coll ector<T,?, Li st<T>> tolist()
static <T> Coll ector<T,?, Set<T>> toSet()
// 产生一个将元素收集到列表或集中的收集器。
static <T,C extends Collection<T>> Collector<T,? , C> toCollection(Supplier<C> collectionFactory)
// 产生一个将元素收集到任意集合中的收集器。可以传递一个诸如TreeSet: : new 的构造器引用。
static Collector<CharSequence, ?, String> joining()
static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix , CharSequence suffix)
// 产生一个连接字符串的收集器。分隔符会置于字符串之间,而第一个字符串之前可以有前缀,最后一个字符串之后可以有后缀。如果没有指定,那么它们都为空。
static <T> Collector<T, ?, IntSummaryStatistics> summarizinglnt(TolntFunction< ? super T> mapper)
static <T> Collector<T, ?, LongSummaryStatistics> summarizinglong(TolongFunction< ? super T> mapper)
static <T> Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction< ? super T> mapper)
// 产生能够生成(Int!Long!Double)SummaryStatistics 对象的收集器,通过它可以获得将mapper 应用于每个元素后所产生的结果的个数、总和、平均值、最大值和最小值。

java.util.(IntSummaryStatistics/DoubleSummaryStatistics/LongSummaryStatistics)

long getCount()
// 产生汇总后的元素的个数。
(int|long|double) getSum()
double getAverage()
// 产生汇总后的元素的总和或平均值,或者在没有任何元素时返回0 。
(int|long|double) getMax()
(int|long|double) getMin()
// 产生汇总后的元素的最大值和最小值,或者在没有任何元素时, 产生(Integer|Long|Double).(MAX|MIN)_VALUE。

1.9 收集到映射表中

java.util.stream.Collector 8

-> static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function< ? super T, ? extends K> keyMapper, Function< ? super T, ? extends U> valueMapper)
-> static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function< ? super T, ? extends K> keyMapper, Function< ? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)
-> static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function< ? super T, ? extends K> keyMapper, Function< ? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
-> static <T, K, U> Collector<org.apache.poi.ss.formula.functions.T, ?, ConcurrentMap<K, U>> toConcurrentMap(Function< ? super T, ? extends K> keyMapper, Function< ? super T, ? extends U> valueMapper)
-> static <T, K, U> Collector<T, ?, ConcurrentMap<K, U>> toConcurrentMap(Function< ? super T, ? extends K> keyMapper, Function< ? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)
-> static <T, K, U, M extends ConcurrentMap<K, U>> Collector<T, ?, M> toConcurrentMap(Function< ? super T, ? extends K> keyMapper, Function< ? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
// 产生一个收集器,它会产生一个映射表或并发映射表。keyMapper 和valueMapper 函数会应用千每个收集到的元素上, 从而在所产生的映射表中生成一个键/值项。默认情况下, 当两个元素产生相同的键时, 会抛出一个IllegalStateException 异常。你可以提供一个mergeFunction 来合并具有相同键的值。默认情况下,其结果是一个HashMap 或ConcurrentHashMap 。你可以提供一个mapSupp1ier , 它会产生所期望的映射表实例。

1.10 群组和分区

java.util.stream.Collector 8

static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function< ? super T, ? extends K> classifier)
static <T, K> Collector<T, ?, ConcurrentMap<K, List<T>>> groupingByConcurrent(Function< ? super T, ? extends K> classifier)
// 产生一个收集器,它会产生一个映射表或并发映射表,其键是将classifier 应用千所有收集到的元素上所产生的结果,而值是由具有相同键的元素构成的一个个列表。
static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate< ? super T> predicate)
// 产生一个收集器,它会产生一个映射表,其键是t rue/false , 而值是由满足/ 不满足断言的元素构成的列表。

1.11 下游收集器

java.util.stream.Collector 8

static <T> Collector<T,?, Long> counting()
// 产生一个可以对收集到的元素进行计数的收集器。
static <T> Collector<T,?, Integer> summingInt(ToIntFunction< ? super T> mapper)
static <T> Collector<T,?, Long> summingLong(ToLongFunction< ? super T> mapper)
static <T> Collector<T,?, Double> summingDouble(ToDoubleFunction< ? super T> mapper)
// 产生一个收集器, 对将mapper 应用到收集到的元素上之后产生的值计算总和。
static <T> Collector<T,?,Optional<T>> maxBy(Comparator< ? super T> comparator)
static <T> Collector<T , ?, Optional <T>> minBy(Comparator< ? super T> comparator)
// 产生一个收集器,使用comparator 指定的排序方法,计算收集到的元素中的最大值 和最小值。
static <T , U,A, R> Collector<T,? , R> mapping(Function< ? super T, ? extends U> mapper, Collector< ? super U,A , R> downstream)
// 产生一个收集器,它会产生一个映射表,其键是将mapper 应用到收集到的数据上而产生的,其值是使用downstream 收集器收集到的具有相同键的元素。

1.12 约简操作

java.util.stream.Stream 8

Optional <T> reduce(BinaryOperator<T> accumulator)
T reduce(Tidentity, BinaryOperator<T> accumulator)
<U> U reduce(U identity , BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
// 用给定的accumulator 函数产生流中元素的累积总和。如果提供了么元,那么第一个被累计的元素就是该么元。如果提供了组合器,那么它可以用来将分别累积的各个部分整合成总和。
<R> Rcollect(Supplier<R> supplier , BiConsumer<R ,? super T> accumulator, BiConsumer<R, R> combiner)
// 将元素收集到类型R 的结果中。在每个部分上,都会调用supplier 来提供初始结果,调用accumulator 来交替地将元素添加到结果中,并调用combiner 来整合两个结果。

1.13 基本类型流

java.util.stream.IntStream 8

static IntStream range(int startlnclusive , int endExclusive)
static IntStream rangeClosed(int startlnclusive , int endlnclusive)
// 产生一个由给定范围内的整数构成的IntStream 。
static IntStream of(int... values)
// 产生一个由给定元素构成的IntStream 。
int[] toArray()
// 产生一个由当前流中的元素构成的数组。
int sum()
OptionalDouble average()
OptionalInt max()
Optionalint min()
IntSummaryStatistics summaryStatistics()
// 产生当前流中元素的总和、平均值、最大值和最小值,或者从中可以获得这些结果的所有四种值的对象。
Stream<Integer> boxed()
// 产生用千当前流中的元素的包装器对象流。

java.util.stream.LongStream 8

static LongStream range(long startlnclusive , long endExclusive)
static LongStream rangeClosed(long startlnclusive , long endlnclusive)
// 产生一个由给定范围内的整数构成的LongStream 。
static LongStream of(long... values)
// 产生一个由给定元素构成的LongStream 。
long[] toArray()
// 产生一个由当前流中的元素构成的数组。
long sum()
OptionalDouble average()
OptionalLong max()
Optionallong min()
LongSummaryStatistics summaryStatistics()
// 产生当前流中元素的总和、平均值、最大值和最小值,或者从中可以获得这些结果的所有四种值的对象。
Stream<Longeger> boxed()
// 产生用千当前流中的元素的包装器对象流。

java.util.stream.DoubleStream 8

static DoubleStream range(double startlnclusive , double endExclusive)
static DoubleStream rangeClosed(double startlnclusive , double endlnclusive)
// 产生一个由给定范围内的整数构成的DoubleStream 。
static DoubleStream of(double... values)
// 产生一个由给定元素构成的DoubleStream 。
double[] toArray()
// 产生一个由当前流中的元素构成的数组。
double sum()
OptionalDouble average()
OptionalDouble max()
Optionaldouble min()
DoubleSummaryStatistics summaryStatistics()
// 产生当前流中元素的总和、平均值、最大值和最小值,或者从中可以获得这些结果的所有四种值的对象。
Stream<Doubleeger> boxed()
// 产生用千当前流中的元素的包装器对象流。

java.lang.CharSequence 1.0

IntStream codePoints() 8
// 产生由当前字符串的所有Unicode 码点构成的流。

java.util.Random 1.0

IntStream ints()
IntStream ints(int randomNumberOrigin, int randomNumberBound) 8
IntStream ints(long streamSize) 8
IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) 8
LongStream longs () 8
LongStream longs(long randomNumberOrigin, long randomNumberBound) 8
longStream longs(long streamSize) 8
LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) 8
DoubleStream doubles () 8
DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) 8
DoubleStream doubles(long streamSize) 8
DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) 8
// 产生随机数流。如果提供了streamSize , 这个流就是具有给定数量元素的有限流。当提供了边界时,其元素将位千randomNumberOrigin (包含)和randomNumberBound(不包含) 的区间内。

java.util.Optional 8

static Optional(Int|Long|Double) of((int|long|double) value)
// 用所提供的基本类型值产生一个可选对象。
(int|long|double) getAs(Int|Long|Double) ()
// 产生当前可选对象的值,或者在其为空时抛出一个NoSuchElementException 异常。
(int|long|double) orElse((int|long|double) other)
(int|long|double) orElseGet((int|long|double)Supplier other)
// 产生当前可选对象的值,或者在这个对象为空时产生可替代的值。
void ifPresent((Int|Long|Double)Consumer consumer)
// 如果当前可选对象不为空,则将其值传递给consumer 。

java.utit.(Int|Long|Double) SummaryStatistics

long getCount()
(int|long|double) getSum()
double getAverage()
(int|long|double) getMax()
(int|long|double) getMin()
// 产生收集到的元素的个数、总和、平均值、最大值和最小值。

1.14 并行流

java.util.stream.BaseStream<T, S extends BaseStream<T, S>> 8

S parallel()
// 产生一个与当前流中元素相同的并行流。
S unordered()
// 产生一个与当前流中元素相同的无序流。

java.util.stream.Collection 1.2

Stream<E> parallelStream() 8
// 用当前集合中的元素产生一个并行流。
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区