Java8新特性(二)——强大的Stream API
一、强大的Stream API
除了Lambda表达式外,Java8另外一项重大更新便是位于java.util.stream.*下的Stream API
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对 集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数 据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
什么是Stream
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。 “集合讲的是数据,流讲的是计算!
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
接下来从三个流程讲解Stream的用法:创建——中间操作——终止操作
如何创建Stream
通过Collection集合家族的方法创建流
default Stream stream() : 返回一个顺序流
default Stream parallelStream() : 返回一个并行流
通过数组的静态方法(Arrays.stream())
static Stream stream(T[] array): 返回一个流
通过Stream的静态方法of()
public static Stream of(T... values) : 返回一个流
通过静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
迭代:public static Stream iterate(final T seed, final UnaryOperator f)
生成: public static Stream generate(Supplier s) :
对以上这些方式进行实例演示:
@Test
public void test1() {
// 1.通过集合得到流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
// 2.通过数组
Employee[] emps = new Employee[5];
Stream<Employee> stream1 = Arrays.stream(emps);
// 3.通过Stream的静态方法
Stream<String> stream2 = Stream.of("a", "b", "c");
// 4.通过Stream静态方法创建无限流
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
Stream<Double> stream4 = Stream.generate(Math::random);
}
中间操作
多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”。
大致可以分为:筛选与切片、映射、排序
我们依旧i通过示例来了解这几个操作:(结果已经通过测试,不再赘述)
// filter——通过Lambda表达式排除流中某些元素
@Test
public void test2() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小张", 18));
empList.add(new Employee("小明", 19));
empList.add(new Employee("小红", 20));
// 集合创建流
Stream<Employee> empStream = empList.stream().filter((e) -> e.getAge() > 18);
// 终止操作
empStream.forEach(System.out::println);
} // limit——截断流,使流不超过指定数量(与skip互补,暂不演示)
@Test
public void test3() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小张", 18));
empList.add(new Employee("小明", 19));
empList.add(new Employee("小红", 20));
// 注意这种写法
empList.stream().filter((e) -> e.getAge() < 20)
.limit(1)
.forEach(System.out::println);
}
// distinct——通过hashCode()和equals()实现去重
@Test
public void test4() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小张", 18));
empList.add(new Employee("小张", 18));
empList.add(new Employee("小红", 20));
// 去重操作
empList.stream()
.filter((e) -> e.getAge() < 19)
.distinct()
.forEach(System.out::println); }
映射
示例讲解:
// map——接收函数,将每个元素运用到函数上,映射为一个新的元素(flatMap见定义,不再赘述)
@Test
public void test5() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小张", 18));
empList.add(new Employee("小明", 19));
empList.add(new Employee("小红", 20));
// 映射操作,例如提取名字
empList.stream()
.map(Employee::getName)
.forEach(System.out::println); }
排序
示例讲解
// sorted——可以按自然排序(无参)或相应的比较器排序(参数为比较器)
@Test
public void test6() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小明", 18));
empList.add(new Employee("小张", 19));
empList.add(new Employee("小红", 20));
// 注意Employee没有自然排序方式!
empList.stream()
.sorted((e1, e2) -> {
// 若年龄相等,比较姓名
if (e1.getAge().equals(e2.getAge())) {
return e1.getName().compareTo(e2.getName());
}
// 年龄不等,直接比较年龄(加上符号,逆向排序)
return -(e1.getAge().compareTo(e2.getAge()));
})
.forEach(System.out::println); }
终止操作
查找与匹配
// allMatch——检查是否全部匹配
// anyMatch——是否至少匹配一个
// noneMatch不再赘述
@Test
public void test7() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小明", 18, Status.FREE));
empList.add(new Employee("小张", 19, Status.VOCATION));
empList.add(new Employee("小红", 20, Status.FREE));
// 注意Employee没有自然排序方式!
boolean b1 = empList.stream()
.allMatch((e) -> e.getStatus().equals(Status.FREE));
System.out.println(b1);// false
boolean b2 = empList.stream()
.anyMatch((e) -> e.getStatus().equals(Status.VOCATION));
System.out.println(b2);// true
}
// findFirst——流中第一个元素
// findAny——返回任意元素
@Test
public void test8() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小明", 18, Status.FREE));
empList.add(new Employee("小张", 19, Status.VOCATION));
empList.add(new Employee("小红", 20, Status.FREE));
// 注意Employee没有自然排序方式!
Optional<Employee> op = empList.stream()
.sorted((e1, e2) -> e1.getAge().compareTo(e2.getAge()))
.findFirst();
// Java8中使用Optional来避免空指针,orElse表示若op元素为空,则使用另外元素替代
// op.orElse(new Employee("老王", 20, Status.FREE));
Employee employee = op.get();
System.out.println(employee); Optional<Employee> any = empList.stream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
}
// count、max、min有点类似SQL语句
@Test
public void test9() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小明", 18, Status.FREE));
empList.add(new Employee("小张", 19, Status.VOCATION));
empList.add(new Employee("小红", 20, Status.FREE));
// 这里省略了中间操作
long count = empList.stream()
.count();
Optional<Employee> max = empList.stream()
.max((e1, e2) -> {
if (e1.getAge().equals(e2.getAge())) {
return e1.getName().compareTo(e2.getName());
}
return e1.getAge().compareTo(e2.getAge());
});
Employee employee = max.get();
System.out.println(employee);
// 提取最小的工资数(结合之前的中间操作)
Optional<Integer> min = empList.stream()
.map(Employee::getAge)
.min(Integer::compare);
System.out.println(min.get());
}
// forEach之前已经使用到,不再赘述
规约
示例:
// reduce——将集合中的元素反复结合起来,得到一个值
@Test
public void test10() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小张", 18));
empList.add(new Employee("小明", 19));
empList.add(new Employee("小红", 20)); // 例如,计算所有年龄总和(先将员工信息进行映射,提取age,也就是经典的map-reduce模式)
Integer totalAge = empList.stream()
.map(Employee::getAge)
.reduce(0, (x, y) -> x + y);// 第一个参数是起始值,第二个参数为二元运算的Function
System.out.println(totalAge);
}
收集
Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态 方法,可以方便地创建常见收集器实例,
示例
// collect——常见的运用例如提取员工信息中的name,组装成新的集合等操作
@Test
public void test11() {
List<Employee> empList = new ArrayList<>();
empList.add(new Employee("小张", 18));
empList.add(new Employee("小明", 19));
empList.add(new Employee("小红", 20)); List<String> list = empList.stream()
.map(Employee::getName)
.collect(Collectors.toList());// 需要去重,请使用toSet();
list.forEach(System.out::println);
// 需要自定义集合,例如LinedHashSet,可以使用此方式
empList.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
}
更多Collectors中的方法(例如couonting,grouping),可以参见下表或者在源码中参考
toList List<T> 把流中元素收集到List
List<Employee> emps= list.stream().collect(Collectors.toList());
toSet Set<T> 把流中元素收集到Set
Set<Employee> emps= list.stream().collect(Collectors.toSet());
toCollection Collection<T> 把流中元素收集到创建的集合
Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new));
counting Long 计算流中元素的个数
long count = list.stream().collect(Collectors.counting());
summingInt Integer 对流中元素的整数属性求和
inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt Double 计算流中元素Integer属性的平均
值
doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。
如:平均值
IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining String 连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy Optional<T> 根据比较器选择最大值
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minBy Optional<T> 根据比较器选择最小值
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducing 归约产生的类型 从一个作为累加器的初始值
开始,利用BinaryOperator与
流中元素逐个结合,从而归
约成单个值
inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结
果转换函数
inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy Map<K, List<T>> 根据某属性值对流分组,属
性为K,结果为V
Map<Emp.Status, List<Emp>> map= list.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy Map<Boolean, List<T>> 根据true或false进行分区
Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));
二、并行流与串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流。
原理——fork/join框架
采用 “工作窃取”模式(work-stealing): 当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线 程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
更多fork/join相关的介绍,请参见:http://www.infoq.com/cn/articles/fork-join-introduction
Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
Long sum = LongStream.rangeClosed(0L, 10000000000L)
.parallel()
.sum();
完整的示例,请参见如下:
package com.atguigu.java8; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream; import org.junit.Test; public class TestForkJoin { @Test
public void test1(){
long start = System.currentTimeMillis(); ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinCalculate(0L, 10000000000L); long sum = pool.invoke(task);
System.out.println(sum); long end = System.currentTimeMillis(); System.out.println("耗费的时间为: " + (end - start)); //112-1953-1988-2654-2647-20663-113808
} @Test
public void test2(){
long start = System.currentTimeMillis(); long sum = 0L; for (long i = 0L; i <= 10000000000L; i++) {
sum += i;
} System.out.println(sum); long end = System.currentTimeMillis(); System.out.println("耗费的时间为: " + (end - start)); //34-3174-3132-4227-4223-31583
} @Test
public void test3(){
long start = System.currentTimeMillis(); Long sum = LongStream.rangeClosed(0L, 10000000000L)
.parallel()
.sum(); System.out.println(sum); long end = System.currentTimeMillis(); System.out.println("耗费的时间为: " + (end - start)); //2061-2053-2086-18926
} }
Java8新特性(二)——强大的Stream API的更多相关文章
- Java8 新特性2——强大的Stream API
强大的Stream API Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找.过滤和映射数据等操作.简而言之,Stream API 提供 ...
- Java8新特性第3章(Stream API)
Stream作为Java8的新特性之一,他与Java IO包中的InputStream和OutputStream完全不是一个概念.Java8中的Stream是对集合功能的一种增强,主要用于对集合对象进 ...
- java8新特性-lambda表达式和stream API的简单使用
一.为什么使用lambda Lambda 是一个 匿名函数,我们可以把 Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...
- Java8 新特性之集合操作Stream
Java8 新特性之集合操作Stream Stream简介 Java 8引入了全新的Stream API.这里的Stream和I/O流不同,它更像具有Iterable的集合类,但行为和集合类又有所不同 ...
- Java8 新特性之Stream----java.util.stream
这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...
- Java8新特性时间日期库DateTime API及示例
Java8新特性的功能已经更新了不少篇幅了,今天重点讲解时间日期库中DateTime相关处理.同样的,如果你现在依旧在项目中使用传统Date.Calendar和SimpleDateFormat等API ...
- Java8新特性之方法引用&Stream流
Java8新特性 方法引用 前言 什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即:非运行 ...
- Java8新特性(1)—— Stream集合运算流入门学习
废话,写在前面 好久没写博客了,懒了,以后自觉写写博客,每周两三篇吧! 简单记录自己的学习经历,算是对自己的一点小小的督促! Java8的新特性很多,比如流处理在工作中看到很多的地方都在用,是时候扔掉 ...
- JAVA8新特性--集合流操作Stream
原文链接:https://blog.csdn.net/bluuusea/article/details/79967039 Stream类全路径为:java.util.stream.Stream 对St ...
- JDK 8 新特性之函数式编程 → Stream API
开心一刻 今天和朋友们去K歌,看着这群年轻人一个个唱的贼嗨,不禁感慨道:年轻真好啊! 想到自己年轻的时候,那也是拿着麦克风不放的人 现在的我没那激情了,只喜欢坐在角落里,默默的听着他们唱,就连旁边的妹 ...
随机推荐
- 每天一个linux命令(21):chgrp,chown,chmod
这三个命令都是改变文件属性与权限的,就放一起写了 charp:改变文件所属用户组 chown:改变文件所属者 chmod:改变文件的权限 一个文件对于owner,group ,others有不同的权限 ...
- SPOJ 694 不同子串个数
一个论文题,求一个字符串有多少个不同的子串. 每个字符串可以看做一个后缀的前缀,然后,就转换为求每一个后缀中,不同的子串有多少. 每一个后缀,根据长度,可以提供len - sa[i] 个子串,但是,画 ...
- CF549BLooksery Party题解
题目描述 The Looksery company, consisting of nn staff members, is planning another big party. Every empl ...
- PHP设计模式——观察者模式
PHP版本 <?php /** * 观察者模式 * 观察者模式能够便利的创建查看目标对象状态的对象,并且提供与核心对象非耦合的指定性功能. * * 为软件添加由某个动作或状态变化激活的,但是松散 ...
- JavaEE权限管理系统的搭建(八)--------角色的增删改
如下图所示,添加角色的同时,要给角色分配权限菜单,关于权限数的显示,我实现了两种方式,普通方式和Ztree方式, 普通方式展示树: 主要代码部分: /** * 进入角色添加页面 * @param mo ...
- 复制D:\\day05目录下的所有文件到D:\\copy,并将.txt文件改为.java文件。
**解题思路: 1.首先定义一个静态的refile方法,参数传入两个文件路径 2.要复制目录下的所有文件,首先查询File类的方法,可以使用listFiles方法得到目录下的文件 3.想到这问题基本就 ...
- Unity 游戏框架搭建 (九) 减少加班利器-QConsole
为毛要实现这个工具? 在我小时候,每当游戏在真机运行时,我们看到的日志是这样的. 没高亮啊,还有乱七八糟的堆栈信息,好干扰日志查看,好影响心情. 还有就是必须始终连着usb线啊,我想要想躺着测试... ...
- HIbernate jar包
密码nbbk https://pan.baidu.com/share/init?surl=nYNO1f20FWMQiZ7iN11DIA
- 网页后缀html、htm、shtml、shtm有什么区别?
每一个网页或者说是web页都有其固定的后缀名,不同的后缀名对应着不同的文件格式和不同的规则.协议.用法,最常见的web页的后缀名是.html和.htm,但这只是web页最基本的两种文件格式,今天我们来 ...
- jquery mobile 移动web(5)
有序列表 <div data-role="content"> <ol data-role="listview" data-theme=&quo ...