一、Java SE 8的流库
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 方法是终止操作
-
流与集合
- 流并不存储其元素。这些元素可能存储在底层的集合中,或者是按需生成的。
- 流的操作不会修改其数据源。例如,filter 方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。
- 流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。例如,如果我们只想查找前5 个长单词而不是所有长单词,那么filter 方法就会在匹配到第5 个单词后停止过滤。因此,我们甚至可以操作无限流。
-
操作流时的典型流程
- 创建一个流。
- 指定将初始流转换为其他流的中间操作,可能包含多个步骤。
- 应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。从此之后,这流就再也不能用了。
1.2 流的创建
-
数组与流
-
使用静态的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 方法
-
**filter **: filter 转换会产生一个流,它的元素与某种条件相匹配。
List<String> words = ...; long count = words.parallelStream().filter(w -> w.length() > 12).count();
filter 的引元是Predicate
(断言接口,对一个对象或是一个基本数据作出判断,要么判断为 true ,要么判断为 false) ,即从T 到boolean 的函数。 -
map:map会按照某种方式来转换流中的值。在使用map 时,会有一个函数应用到每个元素上,并且其结果是包含了应用该函数后所产生的所有结果的流。
Stream<String> lowercaseWords = words.stream().map(String::toLowerCase); // 或 Stream<String> lowercaseWords = words.stream().map(s -> s.substring(0, 1))
-
**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 抽取子流和连接流
-
抽取子流
-
调用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 其他的流转换
-
distinct 方法会返回一个流,它的元素是从原有流中产生的,即原来的元素按照同样的顺序剔除重复元素后产生的。这个流显然能够记住它已经看到过的元素。
Stream<String> uniqueWords = Stream.of("merrily", "merrily", "merrily", "gently").distinct();
-
对于流的排序,有多种sorted 方法的变体可用。其中一种用千操作Comparable 元素的流,而另一种可以接受一个Comparator 。
// 对字符串排序,使得最长的字符串排在最前面 Stream<String> longestFirst = words.stream().sorted(Comparator.comparing(String::length).reversed());
-
peek 方法会产生另一个流,它的元素与原来流中的元素相同,但是在每次获取一个元素时,都会调用一个函数。
// 当实际访问一个元素时,就会打印出来一条消息,主要用于调试 Object[] powers = Stream.iterate(1.0, p -> p * 2).peek(e -> System.out.println("Fetching" + e)).limit(20).toArray();
1.6 简单约简
- 约简是一种终结操作( terminaloperation) ,它们会将流约简为可以在程序中使用的非流值。
-
简单约简方法
-
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 。
-
如何使用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);
-
-
不适合使用Optional 值的方式
-
如果没有正确地使用Optional 值,那么相比较以往的得到“某物或null " 的方式,你并没有得到任何好处。
-
get 方法会在Optional 值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个NoSuchElementException 对象。
Optional<T> optionalValue = ...; optionalValue.get().someMethod(); // 并不比下面的方式更安全 T value = ...; value.someMethod();
-
-
创建Optional 值
- Optional.of(value) 以value 创建一个Optional
- Optional.empty( )创建一个空的Optional
- ofNullable 方法被用来作为可能出现的null 值和可选值之间的桥梁。Optional.ofNullable(obj) 会在obj 不为null 的情况下返回 Optional.of(obj) ,否则会返回 Optional.empty( )。
-
用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 收集到映射表中
-
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 群组和分区
-
将具有相同特性的值群聚成组是非常常见的,并且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 。
-
当分类函数是断言函数( 即返回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 下游收集器
-
groupingBy 方法会产生一个映射表,它的每个值都是一个列表。如果想要以某种方式来处理这些列表,就需要提供一个“下游收集器” 。如果想要获得集而不是列表,那么可以使用上一节中看到的Collector . toSet 收集器
Map<String, Set<Locale>> countryToLocaleSet = locales.collect( Collectors.groupingBy(Locale::getCountry, Collectors.toSet()));
-
counting 会产生收集到的元素的个数。
// 对每个国家有多少个Local e 进行计数 Map<String, Long> countryToLocaleCounts = locales.collect( Collectors.groupingBy(Locale::getCountry, Collectors.counting()));
-
summing(Int I Long I Double) 会接受一个函数作为引元,将该函数应用到下游元素中,并产生它们的和。
// 计算城市流中每个州的人口总和 Map<String, Integer> countryToLocaleCounts = cities.collect( Collectors.groupingBy(City::getState, Collectors.summingInt(City::getPopulation)));
-
maxBy 和minBy 会接受一个比较器,并产生下游元素中的最大值和最小值。
// 产生每个州中最大的城市 Map<String, OptionalCity<City>> countryToLocaleCounts = cities.collect( Collectors.groupingBy(City::getState, Collectors.maxBy(Comparator.comparing(City::getPopulation))))
-
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())));
-
如果群组和映射函数的返回值为int 、long 或d ouble , 那么可以将元素收集到汇总统计对象中,
Map<String, Integer> countryToLocaleCounts = cities.collect( Collectors.groupingBy(City::getState, Collectors.summarizingInt(City::getPopulation))); // 可以从每个组的汇总统计对象中获取这些函数值的总和、个数、平均值、最小值和最大值。
-
还有3 个版本的reducing 方法,它们都应用了通用的约简操作,下一节讲解
1.12 约简操作
-
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 种形式
// 如果流为空, 则会返回0,即不用处理Optional类 List<Integer> values = ...; Optional<Integer> sum = values.stream().reduce(0, Integer::sum);
-
首先提供一种“累积器”函数(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, 它会接受单个引元:
-
一个提供者,它会创建目标类型的新实例,例如散列集的构造器。
-
一个累积器,它会将一个元素添加到一个实例上,例如add 方法。
-
一个组合器,它会将两个实例合并成一个,例如addAll 。
-
下面的代码展示了collect 方法是如何操作位集的:
BitSet result = stream.collect(BitSet::new, BitSet::set, BitSet::or);
-
1.13 基本类型流
-
流库中具有专门的类型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 方法
-
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
-
CharSequence 接口拥有codePoints 和chars 方法,可以生成由字符的Unicode 码或由UTF-16 编码机制的码元构成的IntStream 。
IntStream codes = sentence.codePoints();
-
当你有一个对象流时,可以用mapToint 、mapTolong 和mapToDouble 将其转换为基本类型流。
Strea111<String> words = ...; IntStream lengths= words.mapTo!nt(String::length);
-
为了将基本类型流转换为对象流,需要使用boxed 方法
Stream<Integer> integers = IntStream.range (0, 100).boxed();
-
基本类型流上的方法与对象流上的方法类似。其差异如下
-
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 并行流
-
流使得并行处理块操作变得很容易。这个过程几乎是自动的,但是需要遵守一些规则。首先,必须有一个并行流。可以用Collec t ion.parallelStream( ) 方法从任何集合中获取一个并行流:
Stream<String> parallelWords = words.parallelStream();
parallel 方法可以将任意的顺序流转换为并行流。只要在终结方法执行时,流处于并行模式,那么所有的中间流操作都将被并行化。
Stream<String> parallelWords = Stream.of(wordArray).parallel();
-
对字符串流中的所有短单词计数
// Map<Integer, Long> shortwordCounts = words .parallelStream() .filter(s -> s.length() < 10) .collect(Collectors.groupingBy(String::length, Collectors.counting()));
警告:
传递给并行流操作的函数不应该被堵塞。并行流使用fork-join 池来操作流的各个部分。如果多个流操作被阻塞,那么池可能就无法做任何事情了。
-
流顺序
-
从有序集合(数组和列表)、范围、生成器和迭代产生的流,或者通过调用Stream . sorted 产生的流,都是有序的。
-
排序并不排斥高效的并行处理。如,当计算stream.map(fun) 时, 流可以被划分为n 的部分,它们会被并行地处理。
-
当放弃排序需求时,有些操作可以被更有效地并行化。通过在流上调用unordered 方法,就可以明确表示我们对排序不感兴趣。
-
还可以通过放弃排序要求来提高limit 方法的速度。
// 如果只想从流中取出任意n 个元素,而并不在意到底要获取哪些 Stream<String> sample = words.parallelStream().unordered().limt(n) ;
-
-
合并映射表的代价很高昂。正是因为这个原因, Collectors.groupByConcurrent 方法使用了共享的并发映射表。为了从并行化中获益,映射表中值的顺序不会与流中的顺序相同。
Map<Integer, List<String>> result = words.para11e1Stream().collect( Collectors.groupingByConcurrent(String::length));
不要修改在执行某项流操作后会将元素返回到流中的集合(即使这种修改是线程安全的) 。记住,流并不会收集它们的数据,数据总是在单独的集合中。如果修改了这样的集合,那么流操作的结果就是未定义的。更准确地讲,因为中间的流操作都是惰性的,所以直到执行终结操作时才对集合进行修改仍旧是可行的。
-
为了让并行流正常工作,需要满足大量的条件:
- 数据应该在内存中。必须等到数据到达是非常低效的。
- 流应该可以被高效地分成若干个子部分。由数组或平衡二叉树支撑的流都可以工作得很好,但是Stream.iterate 返回的结果不行。
- 流操作的工作量应该具有较大的规模。如果总工作负载并不是很大,那么搭建并行计算时所付出的代价就没有什么意义。
- 流操作不应该被阻塞。
-
不要将所有的流都转换为并行流。只有在对已经位千内存中的数据执行大量计算操作时,才应该使用并行流。
API注释
1.1 从迭代到流的操作
java.util.stream.Stream
Stream<T> filter(Predicate< ? super T> p)
// 产生一个流, 其中包含当前流中满足P 的所有元素。
long count()
// 产生当前流中元素的数量。这是一个终止操作。
java.util.Collection
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
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
Stream<E> parallelStream() 8
// 用当前集合中的元素产生一个并行流。
评论区