流的操做类型分为两种:java
Intermediate:一个流能够后面跟随零个或多个 intermediate 操做。其目的主要是打开流,作出某种程度的数据映射/过滤,而后返回一个新的流,交给下一个操做使用。这类操做都是惰性化的(lazy),就是说,仅仅调用到这类方法,并无真正开始流的遍历。 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered数组
Terminal:一个流只能有一个 terminal 操做,当这个操做执行后,流就被使用“光”了,没法再被操做。因此这一定是流的最后一个操做。Terminal 操做的执行,才会真正开始流的遍历,而且会生成一个结果,或者一个 side effect forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator缓存
Short-circuiting:对一个无限及集合返回有限的数据 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limitbash
对象匹配过滤数据结构
方法 | 示意 |
---|---|
filter | 经过传递一个预期匹配的对象做为参数并返回一个包含全部匹配到的对象的流。 |
distinct | 返回包含惟一元素的流(惟一性取决于元素相等的实现方式)。 |
limit | 返回一个特定上限的流 |
skip | 返回一个丢弃前n个元素的流 |
List expensiveInvoices
= invoices.stream()
.filter(inv -> inv.getAmount() > 10_000)
.limit(5)
.collect(Collectors.toList());
复制代码
匹配是一个判断是否匹配到给定属性的广泛的数据处理模式,表达式最后返回boolean架构
方法 | 示意 |
---|---|
allMacth | 流对象每一个都要匹配到 |
anyMatch | 流对象有匹配到就中断 |
noneMatch | 流对象无匹配对象 |
boolean expensive =
invoices.stream()
.allMatch(inv -> inv.getAmount() > 1_000);
复制代码
:流接口还提供了像findFirst和findAny等从流中取出任意的元素。它们能与像filter方法相链接。findFirst和findAny都返回一个可选对象,在串行流中二者都是返回第一个对象,在并行流中findAny返回第一个线程处理最快的数据app
方法 | 示意 |
---|---|
findFirst | 返回第一个匹配到的对象 |
findAny | 返回第一个匹配到的对象 |
Optional =
invoices.stream()
.filter(inv ->
inv.getCustomer() == Customer.ORACLE)
.findAny();
复制代码
:流支持映射方法,传递一个函数对象做为方法,把流中的元素转换成另外一种类型。这种方法应用于单个元素,将其映射成新元素。框架
方法 | 示意 |
---|---|
map | 应用于单个元素,将其映射成新元素。 |
List ids
= invoices.stream()
.map(Invoice::getId)
.collect(Collectors.toList());
复制代码
方法 | 示意 |
---|---|
reduce(BinaryOperator) | 一个函数主要用于累和,求最大值。 |
int product = numbers.stream().reduce(1, (a, b) -> a * b);
int max = numbers.stream().reduce(Integer.MIN_VALUE,
Integer::max);
复制代码
forEach Terminal操做,再也不返回stream对象,forEach循环体内不能修改本身包含的本地变量值,也不能用 break/return 之类的关键字提早结束循环。 peek能够对循环对象二次封装并返回新的对象ide
sorted
它比数组的排序更强之处在于你能够首先对 Stream 进行各种 map、filter、limit、skip 甚至 distinct 来减小元素数量后,再排序,这能帮助程序明显缩短执行时间。函数
List<Person> persons = new ArrayList();
for (int i = 1; i <= 5; i++) {
Person person = new Person(i, "name" + i);
persons.add(person);
}
List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());
System.out.println(personList2);
复制代码
获取最后计算的最小值,最大值和去重复
目前为止你所了解的方法都是返回另外一个流或者一个像boolean,int类型的值,或者返回一个可选对象。相比之下,collect方法是一个结束操做,它可使流里面的全部元素汇集到汇总结果。 传递给collect方法参数是一个java.util.stream.Collector类型的对象。Collector对象实际上定义了一个如何把流中的元素汇集到最终结果的方法。最开始,工厂方法Collectors.toList()被用来返回一个描述了如何把流转变成一个List的Collector对象。后来Collectors类又内建了不少类似的collectors变量。例如,你能够用Collectors.groupingBy方法按消费者把发票分组
Map<Customer, List> customerToInvoices
= invoices.stream().collect(Collectors.group
ingBy(Invoice::getCustomer));
复制代码
Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).
limit(100).
collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next();
System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
}
复制代码
在使用条件“年龄小于 18”进行分组后能够看到,不到 18 岁的未成年人是一组,成年人是另一组。partitioningBy 实际上是一种特殊的 groupingBy,它依照条件测试的是否两种结果来构造返回的数据结构,get(true) 和 get(false) 能即为所有的元素对象。
方法 | 示意 |
---|---|
mapToInt | 函数体返回值为int类型 |
mapToDouble | 函数体返回值为Double类型。 |
mapToLong | 函数体返回值为Long类型。 |
方法 | 示意 |
---|---|
flatMapToInt | 函数体返回值为int类型 |
flatMapDouble | 函数体返回值为Double类型。 |
flatMapLong | 函数体返回值为Long类型。 |
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1),
Arrays.asList(2, 3),
Arrays.asList(4, 5, 6)
);
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());
复制代码
Stream API 支持方便的数据并行。换句话说,你能够明确地让流管道以并行的方式运行而不用关心底层的具体实现。在这背后,Stream API使用了Fork/Join框架充分利用了你机器的多核架构。 你所须要作的无非是用parallelStream()方法替换stream()方法。 不是全部的都使用并行的方式,以下