Stream
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
Stream操作步骤如下:
- 创建Stream
在 Java 8 中, 集合接口有两个方法来生成流:
stream() − 为集合创建串行流。
parallelStream() − 为集合创建并行流。
- 中间操作,如map()方法、flatMap()方法、filter()方法,返回的是符合条件的流,可以忽略中间操作,直接进行终止操作。
- 终止操作,allMatch()、findFirst()、count()等方法,返回的是我们需要的具体的结果。
现在我们以具体的代码来分析stream相关的方法api,我们有一个Employee类,并且有一个元素是Employee类型的集合。我们就在代码中体现出stream的用法,我会尽量多写一些注释。
Employee类
package com.wqh.demo.java8;
public class Employee {
private int id;
private String name;
private int age;
private double salary;
private Status status;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public Employee(int id, String name, int age, double salary, Status status) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
this.status = status;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String show() {
return "测试方法引用!";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + ", status=" + status
+ "]";
}
public enum Status {
FREE, BUSY, VOCATION;
}
}
创建Stream和中间操作
package com.wqh.demo.java8;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
/*
* 一、 Stream 的操作步骤
*
* 1. 创建 Stream
*
* 2. 中间操作
*
* 3. 终止操作
*/
public class TestStreamAPI1 {
// Employee类型的集合
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "张三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//2. 中间操作
/*
映射
map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
@Test
public void test1(){
// map()方法:入参要求是一个实现了函数式接口java.util.function.Function接口的对象,可以直接使用lambda表达式进行实现并传入
//返回的是只有name的流
Stream<String> str = emps.stream()
.map((e) -> e.getName());
System.out.println(str);
System.out.println("-------------------------------------------");
List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
// map()方法返回的是每个元素都是经过大写操作的流
Stream<String> stream = strList.stream()
.map(String::toUpperCase);
stream.forEach(System.out::println);
Stream<Stream<Character>> stream2 = strList.stream()
.map(TestStreamAPI1::filterCharacter);
stream2.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("---------------------------------------------");
Stream<Character> stream3 = strList.stream()
.flatMap(TestStreamAPI1::filterCharacter);
stream3.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
/*
中间操作
sorted()——自然排序
sorted(Comparator com)——定制排序
*/
@Test
public void test2(){
// 1、stream()方法获取流
// 2、map()、sorted()中间操作,获取一个只有name并排序完成的流
// 3、forEach() 终止操作,输出流里面的内容
emps.stream()
.map(Employee::getName)
.sorted()
.forEach(System.out::println);
System.out.println("------------------------------------");
// 1、stream()方法获取流
// 2、sorted()中间操作,lambda表达式定义了排序规则,将流里面的内容进行了排序
// 3、forEach() 终止操作,输出流里面的内容
emps.stream()
.sorted((x, y) -> {
if(x.getAge() == y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
}
}
终止操作来得到我们需要的结果
package com.wqh.demo.java8;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.Test;
import com.wqh.demo.java8.Employee.Status;
/*
* 一、 Stream 的操作步骤
*
* 1. 创建 Stream
*
* 2. 中间操作
*
* 3. 终止操作
*/
public class TestStreamAPI2 {
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
//3. 终止操作
/*
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
*/
@Test
public void test1(){
//1、stream()方法创建流
//2、allMatch()方法终止操作,返回流中的数据是否全部都是状态为BUSY的结果
boolean bl = emps.stream()
.allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl);
//1、stream()方法创建流
//2、anyMatch()方法终止操作,返回流中的数据是否存在状态为BUSY的结果
boolean bl1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl1);
//1、stream()方法创建流
//2、anyMatch()方法终止操作,返回流中的数据是否不存在状态为BUSY的结果
boolean bl2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl2);
}
@Test
public void test2(){
//1、stream()方法创建流
//2、sorted()方法根据salary字段进行排序,中间操作,改变了流里面数据的顺序
//3、findFirst()方法终止操作,返回流中的第一条数据,即工资最高的一个数据
Optional<Employee> op = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op.get());
System.out.println("--------------------------------");
//1、stream()方法创建流
//2、filter()方法过滤出所有状态是FREE的数据,中间操作,使流里面只保留了状态是FREE的数据
//3、findAny()方法终止操作,返回流中任意一条状态是FREE的数据
Optional<Employee> op2 = emps.parallelStream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());
}
@Test
public void test3(){
//1、stream()方法创建流
//2、filter()方法过滤出所有状态是FREE的数据,中间操作,使流里面只保留了状态是FREE的数据
//3、count()方法终止操作,返回流中的元素个数
long count = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.count();
System.out.println(count);
//1、stream()方法创建流
//2、map()方法流里面的元素只有salary,中间操作,使原来的流变成了只有salary的流
//3、max()方法终止操作,返回流中的salary最大的元素
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.max(Double::compare);
System.out.println(op.get());
//1、stream()方法创建流
//2、min()方法流里面的元素根据salary排序,并返回最小的那个元素,终止操作。
//此方式也可用于上面的max()方法
Optional<Employee> op2 = emps.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op2.get());
}
//注意:流进行了终止操作后,不能再次使用
@Test
public void test4(){
Stream<Employee> stream = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE));
long count = stream.count();
stream.map(Employee::getSalary)
.max(Double::compare);
}
}
package com.wqh.demo.java8;
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;
import com.wqh.demo.java8.Employee.Status;
public class TestStreamAPI3 {
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 79, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
//3. 终止操作
/*
归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。也可以用作如将一系列数中的正数求和、将序列中满足某个条件的数一起做某些计算、求最大值、最小值等
*/
@Test
public void test1(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//1、stream()方法,获取流
//2、reduce()方法,第一个参数0,初始值,第二个参数:流里面元素计算的公式,第一个元素与初始
//值0相加,相加之和再与第二个元素累加,即不断累计求和
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------------------------------------");
//1、stream()方法,获取流
//2、map()方法,获取由salary组成的流
//3、reduce()方法,只有一个参数:流里面元素计算的公式,第一个元素与
//第二个元素相加,相加之和再与第三个元素累加,即不断累计求和
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
//需求:搜索名字中 “六” 出现的次数
@Test
public void test2(){
//1、stream()方法获取流
//2、map()获取由name组成的新流
//3、flatMap()将流中的每个name元素,转换成一个个的由字符组成的流,然后合并成一个大的字符流
//4、map()将大的字符流里面的字符过一遍,是“六”就返回1,否则返回0,变成了一个只有0和1的新流
//5、reduce()方法进行累加,得到“六”的出现次数
Optional<Integer> sum = emps.stream()
.map(Employee::getName)
.flatMap(TestStreamAPI1::filterCharacter)
.map((ch) -> {
if(ch.equals('六')) {
return 1;
} else {
return 0;
}
}).reduce(Integer::sum);
System.out.println(sum.get());
}
//collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
@Test
public void test3(){
//1、stream()方法获取流
//2、map()获取由name组成的新流
//3、collection(),将流中的name元素形成一个list集合,并返回
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------------------------");
//1、stream()方法获取流
//2、map()获取由name组成的新流
//3、collection(),将流中的name元素形成一个set集合,并返回
//set集合将进行去重
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------------------------");
//1、stream()方法获取流
//2、map()获取由name组成的新流
//3、collection(),将流中的name元素形成一个HashSet集合,并返回
//set集合将进行去重
HashSet<String> hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
}
@Test
public void test4(){
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
}
//分组
@Test
public void test5(){
Map<Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
//多级分组
@Test
public void test6(){
Map<Status, Map<String, List<Employee>>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() >= 60) {
return "老年";
} else if (e.getAge() >= 35) {
return "中年";
} else {
return "成年";
}
})));
System.out.println(map);
}
//分区
@Test
public void test7(){
Map<Boolean, List<Employee>> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
//
@Test
public void test8(){
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("," , "----", "----"));
System.out.println(str);
}
@Test
public void test9(){
Optional<Double> sum = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.reducing(Double::sum));
System.out.println(sum.get());
}
}