Java8的Stream流详解
Stream流的概述
什么是Stream流?
在Java 8中,得益于Lambda所带来的函数式编程, 引入了一个全新的Stream流概念。
目的:用于简化集合和数组操作的API。
Stream流式思想的核心:
1、先得到集合或者数组的Stream流(就是一根传送带)
2、把元素放上去
3、然后就用这个Stream流简化的API来方便的操作元素。


问题1、Stream流的作用是什么,结合了什么技术?
简化集合、数组操作的API。结合了Lambda表达式。
问题2、说说Stream流的思想和使用步骤。
1、先得到集合或者数组的Stream流(就是一根传送带)。
2、把元素放上去。
3、然后就用这个Stream流简化的API来方便的操作元素。
Stream流的获取
Stream流的三类方法:
1、获取Stream流——>创建一条流水线,并把数据放到流水线上准备进行操作.
2、中间方法——>流水线上的操作。一次操作完毕之后,还可以继续进行其他操作.
3、终结方法——>一个Stream流只能有一个终结方法,是流水线上的最后一个操作.
注:Stream操作集合或者数组的第一步是先得到Stream流,然后才能使用流的功能。
集合获取Stream流的方式:可以使用Collection接口中的默认方法stream()生成流
| 名称 | 说明 |
|---|---|
| default Stream<\E> stream() | 获取当前集合对象的Stream流 |
数组获取Stream流的方式:
| 名称 | 说明 |
|---|---|
| public static <\T> Stream<\T> stream(\T[] array) | 获取当前数组的Stream流 |
| public static <\T> Stream<\T> of(\T…values) | 获取当前数组/可变数据的Stream流 |
代码测试验证:
import java.util.*;
import java.util.stream.Stream;
public class StreamDemo2 {
public static void main(String[] args) {
/*------------------Collection集合获取流--------------------*/
Collection<String> list = new ArrayList<>(); // 多态
Stream<String> s = list.stream();
/*------------------Map集合获取流--------------------*/
Map<Integer, String> map = new HashMap<>(); // 多态
// 获取键流
Stream<Integer> keyStream = map.keySet().stream(); // map.keySet(): 获取键的集合
// 获取值流
Stream<String> valueStream = map.values().stream(); // map.values(): 获取值的集合
// 获取键值对流
Stream<Map.Entry<Integer, String>> keyAndValueStream = map.entrySet().stream(); // map.entrySet(): 获取键值对的集合
/*------------------数组获取流--------------------*/
String[] names = {"1", "2"};
// 1.1 数组获取流的方式
Stream<String> namesStream = Arrays.stream(names);
// 1.2 数组获取流的方式
Stream<String> stringStream = Stream.of(names);
}
}
Stream流的常用API
Stream流的常用API(中间操作方法):
注意:
1、中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程。
2、在Stream流中无法直接修改集合、数组中的数据。
Stream流的常见终结操作方法:
| 名称 | 说明 |
|---|---|
| void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
| long count() | 返回此流中的元素数 |
注意:终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。
问题1、终结和非终结方法的含义是什么?
终结方法后流不可以继续使用,非终结方法会返回新的流,支持链式编程。
代码测试验证:
public class StreamDemo3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");
// 1. filter : 过滤元素
//Stream<T> filter(Predicate<? super T> predicate);
// 链式编程,输出结果:张无忌 张强 张三丰 张三丰
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
// count:统计个数,
long size = list.stream().filter(s -> s.length() == 2).count(); // 返回值为long类型而不是int类型
System.out.println(size); // 2
// limit : 取前几个元素
// 链式编程,输出结果:张无忌 张强
list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));
// skip : 跳过前几个
// 链式编程,输出结果:张三丰 张三丰
list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(s -> System.out.println(s));
// map : 加工方法, 第一个参数原材料 --> 第二个参数是加工后的结果
// 链式编程,输出结果:
/**
* xyz张无忌
* xyz周芷若
* xyz赵敏
* xyz张强
* xyz张三丰
* xyz张三丰
*/
list.stream().map(s -> "xyz" + s). forEach(s -> System.out.println(s));
// 把所有的名称都加工成一个学生对象
list.stream().map(s -> new Student(s)).forEach(student -> System.out.println(student));
list.stream().map(Student::new).forEach(System.out::println); // 构造器引用 方法引用(要求前后参数一致)
// concat : 合并流。
// 链式编程,输出结果:
/**
* 周芷若
* Java1
* Java2
*/
Stream<String> s1 = list.stream().filter(s -> s.startsWith("周"));
Stream<String> s2 = Stream.of("Java1", "Java2");
Stream<String> s3 = Stream.concat(s1, s2);
s3.forEach(s -> System.out.println(s));
}
}
Stream流的综合应用
案例需求:某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。
分析:
1、员工信息至少包含了(名称、性别、工资、奖金、处罚记录)
2、开发一部有4个员工、开发二部有5名员工
3、分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer
4、分别统计出2个部门的平均月收入,要求去掉最高和最低工资。
5、统计2个开发部门整体的平均工资,去掉最低和最高工资的平均值。
Employee类
public class Employee {
private String name;
private char sex;
private double salary;
private double bonus;
private String punish; // 处罚记录,如果没有则为null
public Employee() {
}
public Employee(String name, char sex, double salary, double bonus, String punish) {
this.name = name;
this.sex = sex;
this.salary = salary;
this.bonus = bonus;
this.punish = punish;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public String getPunish() {
return punish;
}
public void setPunish(String punish) {
this.punish = punish;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sex=" + sex +
", salary=" + salary +
", bonus=" + bonus +
", punish='" + punish + '\'' +
'}';
}
}
Topperformer类:
public class Topperformer {
private String name;
private double money;
public Topperformer(){}
public Topperformer(String name, double money){
this.name = name;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Topperformer{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
}
测试StreamDemo类:
public class StreamDemo04 {
public static double allMoney = 0;
public static void main(String[] args) {
List<Employee> one = new ArrayList<>();
one.add(new Employee("猪八戒",'男',30000 , 25000, null));
one.add(new Employee("孙悟空",'男',25000 , 1000, "顶撞上司"));
one.add(new Employee("沙僧",'男',20000 , 20000, null));
one.add(new Employee("小白龙",'男',20000 , 25000, null));
List<Employee> two = new ArrayList<>();
two.add(new Employee("武松",'男',15000 , 9000, null));
two.add(new Employee("李逵",'男',20000 , 10000, null));
two.add(new Employee("西门庆",'男',50000 , 100000, "被打"));
two.add(new Employee("潘金莲",'女',3500 , 1000, "被打"));
two.add(new Employee("武大郎",'女',20000 , 0, "下毒"));
Employee oneMax = one.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).get();
System.out.println(oneMax);
Topperformer oneTopperformer = one.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).map(
employee -> new Topperformer(employee.getName(), employee.getBonus() + employee.getSalary())
).get();
System.out.println(oneTopperformer);
Employee twoMax = two.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).get();
System.out.println(twoMax);
Topperformer twoTopperformer = two.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).map(
employee -> new Topperformer(employee.getName(), employee.getBonus() + employee.getSalary())
).get();
System.out.println(twoTopperformer);
System.out.println("----------------------------------------------------------------");
one.stream().sorted((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())). // 升序
skip(1).limit(one.size()-2).forEach(employee -> {
allMoney += (employee.getBonus() + employee.getSalary());
});
System.out.println("部门1的平均工资为:" + allMoney / (one.size() - 2));
allMoney = 0;
two.stream().sorted((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).
skip(1).limit(two.size() - 2).forEach(employee -> {
allMoney += (employee.getBonus() + employee.getSalary());
});
System.out.println("部门2的平均工资为:" + allMoney / (two.size() - 2));
System.out.println("----------------------------------------------------------------");
allMoney = 0;
Stream<Employee> s1_stream = one.stream(); // 需要指定泛型,如果不指定返回的就死Object类型
Stream<Employee> s2_stream = two.stream();
Stream<Employee> s3 = Stream.concat(s1_stream, s2_stream); // 合并两个流
s3.sorted((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).
skip(1).limit(one.size() + two.size() - 2).forEach(employee -> {
allMoney += (employee.getBonus() + employee.getSalary());
});
// BigDecimal精度问题
BigDecimal a = BigDecimal.valueOf(allMoney);
BigDecimal b = BigDecimal.valueOf(one.size() + two.size() - 2);
System.out.println("开发部门平均工资为:" + a.divide(b, 4, RoundingMode.HALF_UP));
}
}
收集Stream流
Stream流的收集操作
收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去。
Stream流:方便操作集合/数组的手段。
集合/数组:才是开发中的目的。
Stream流的收集方法:
| 名称 | 说明 |
|---|---|
| R collect(Collector collector) | 开始收集Stream流,指定收集器 |
Collectors工具类提供了具体的收集方式:
| 名称 | 说明 |
|---|---|
| public static <\T> Collector toList() | 把元素收集到List集合中 |
| public static <\T> Collector toSet() | 把元素收集到Set集合中 |
| public static <\T> Collector toMap() | 把元素收集到Map集合中 |
代码测试验证:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
目标:收集Stream流的数据到 集合或者数组中去。
*/
public class StreamDemo5 {
public static void main(String[] args) {
List<String> list = new ArrayList<>(); // 多态的写法
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
List<String> zhangList = s1.collect(Collectors.toList()); // 可变集合
System.out.println(zhangList); // [张无忌, 张强, 张三丰, 张三丰]
zhangList.add("赵六");
System.out.println(zhangList); // [张无忌, 张强, 张三丰, 张三丰, 赵六]
// stream has already been operated upon or closed
// 特别注意注意注意: stream流只能用一次,要不然会报错:stream has already been operated upon or closed
Stream<String> s2 = list.stream().filter(s -> s.startsWith("赵"));
List<String> luList = s2.collect(Collectors.toList());
System.out.println(luList); // [赵敏]
// List<String> list1 = s1.toList(); // 直接进行toList为不可变集合,只能进行查看操作,不能进行增加,删除,更新操作
// System.out.println(list1);
// list1.add("杨银羽");
System.out.println("----------------------------------------------------");
Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
Set<String> zhangSet = s3.collect(Collectors.toSet());
System.out.println(zhangSet); // [张强, 张三丰, 张无忌]
System.out.println("----------------------------------------------------");
Stream<String> s4 = list.stream().filter(s -> s.startsWith("张"));
Object[] objects = s4.toArray();
System.out.println("数组内容为:" + Arrays.toString(objects)); // 数组内容为:[张无忌, 张强, 张三丰, 张三丰]
}
}