Stream流
创建不可变集合
什么是不可变集合?
- 不可变集合,就是不可被修改的集合。
- 集合的数据项在创建的时候提供,并且在整个生命周期中都不可以被改变。否则报错。
为什么要创建不可变集合?
- 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
- 或者当集合对象被不可信的库调用时,不可变形式是安全的。
如何创建不可变集合?
- 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。
方法名称 | 说明 |
---|---|
static |
创建一个具有指定元素的List集合对象 |
static |
创建一个具有指定元素的Set集合对象 |
static <K,V> Map<K,V> of(E...elements) | 创建一个具有指定元素的Map集合对象 |
public class CollectionDemo {
public static void main(String[] args) {
//1.创建一个不可变的List集合
List<Double> money = List.of(125.5,23.2,431.3,100.0,125.5);
System.out.println(money.get(1));
System.out.println(money);
Set<String> names = Set.of("张三", "李四", "王五");
System.out.println(names);
Map<String, Integer> maps = Map.of("张三",1,"老白",2,"老黑",3);
//maps.put("你爹",22);
System.out.println(maps);
}
}
output:
23.2
[125.5, 23.2, 431.3, 100.0, 125.5]
[王五, 张三, 李四]
{老黑=3, 老白=2, 张三=1}
- 这个集合不能添加,不能删除,不能修改。
Stream流
Stream流的概述
什么是Stream流?
- 在java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念
- 目的:用于简化集合和数组操作的API。结合力Lambda表达式
案例:体验Stream流的作用
需求:按照下面的要求完成集合的创建和遍历
-
创建一个集合存储多个字符串
-
List<String> list = new ArrayList(); list.add("张无忌"); list.add("殷素素"); list.add("赵敏"); list.add("张尼"); list.add("张三丰");
-
把集合中的所有以"张"开头的元素存储到一个新的集合
-
把"张"开头的集合中的长度为3的元素存储到一个新的集合
-
遍历上一步得到的集合中的元素输出
public class StreamTest {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
Collections.addAll(names, "张三丰", "张无忌", "赵敏", "殷素素", "张口");
System.out.println(names);
List<String> zhang = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String s = names.get(i);
if (s.startsWith("张")) {
zhang.add(s);
}
}
System.out.println(zhang);
List<String> zhagnThree = new ArrayList<>();
for (int i = 0; i < zhang.size(); i++) {
String s = zhang.get(i);
if (s.length() == 3){
zhagnThree.add(s);
}
}
System.out.println(zhagnThree);
//使用Stream流
names.stream().filter(s->s.startsWith("张") && s.length()==3).forEach(s-> System.out.println(s));
names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s-> System.out.println(s));
}
output:
[张三丰, 张无忌, 赵敏, 殷素素, 张口]
[张三丰, 张无忌, 张口]
[张三丰, 张无忌]
张三丰
张无忌
张三丰
张无忌
Stream流式思想的核心:
- 先得到集合或者数组的Stream流(就是一根传送带)
- 把元素放上去
- 然后就用这个Stream流简化的API来方便的操作元素
Stream流的获取
Stream流的三类方法
- 获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
- 中间方法
- 流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。
- 终结方法
- 一个Stream流只能有一个终结方法,是流水线上的最后一个操作。
Stream操作集合或者数组,第一步应该先得到Stream流,然后才能使用流的功能
集合获取Stream 流的方式
- 可以使用Collection接口中默认的Stream()方法生成流
方法名称 | 说明 |
---|---|
default Stream |
获取当前集合对象的Stream()流 |
数组获取Stream流的方法
方法名称 | 说明 |
---|---|
public static |
获取当前数组的Stream流 |
public static |
获取当前数组/可变数据的 |
public class StreamDemo2 {
public static void main(String[] args) {
/** ------------------------Collection集合获取流--------------------*/
Collection<String> list = new ArrayList<>();
Stream<String> s = list.stream();
/** ------------------------Map集合获取流--------------------*/
Map<String,Integer> maps = new HashMap<>();
//键流
Stream<String> keyStream = maps.keySet().stream();
//值流
Stream<Integer> valueStream = maps.values().stream();
//键值对流
Stream<Map.Entry<String, Integer>> keyAndValueStream = maps.entrySet().stream();
/** ----------------------------数组获取流----------------------------*/
String[] names = {"甲","不懂","霸占了","属于"};
//方法一
Stream<String> namesStream = Arrays.stream(names);
//方法二
Stream<String> nameStream = Stream.of(names);
}
}
Stream流的常用API
Stream流的常用API(中间操作方法)
方法名称 | 说明 |
---|---|
Stream |
用于对流中的数据进行过滤 |
Stream |
获取前几个元素 |
Stream |
跳过前几个元 |
Stream |
去除流中重复的元素。依赖(hashCode和equals方法) |
static |
合并a,b两个流为一个流 |
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("张强");
// list.stream().filter(s-> s.startsWith("张")).forEach(s -> System.out.println(s));
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
System.out.println("-----------");
long size = list.stream().filter(s -> s.length() == 3).count();
System.out.println(size);
list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println);
System.out.println("-----------");
list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);
System.out.println("-----------");
//Map加工方法
//给集合元素前面都加一个:java
list.stream().map(s -> "java" + s).forEach(s -> System.out.println(s));
System.out.println("-----------");
//需求:把所有的名称 都加工成一个学生对象
list.stream().map(s -> new Student(s)).forEach(student -> System.out.println(student));
// list.stream().map(Student::new).forEach(System.out::println);//构造器引用 方法引用
System.out.println("-----------");
//合并流
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
Stream<String> s2 = Stream.of("java","Java");
//public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
Stream<String> s3 = Stream.concat(s1, s2);
//s3.forEach(s -> System.out.println(s));
s3.distinct().forEach(s -> System.out.println(s));//distinct去重复
}
}
output:
张无忌
张三丰
张三丰
张强
-----------
4
张无忌
张三丰
-----------
张三丰
张强
-----------
java张无忌
java小昭
java周芷若
java张三丰
java张三丰
java张强
-----------
Student{name='张无忌'}
Student{name='小昭'}
Student{name='周芷若'}
Student{name='张三丰'}
Student{name='张三丰'}
Student{name='张强'}
-----------
张无忌
张三丰
张强
java
Java
注意:
- 中间方法也称非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程
- 在Stream流中无法直接修改集合、数组中的数据。
Stream流中常见终结操作的方法
方法名称 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
long count() | 返回此流中元素数 |
注意:终结操作方法,调用完成流就无法继续使用,原因是不会再返回Stream了
Stream流的综合应用
案例:
需求:某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。
分析:
- 员工信息至少包含了(名称、性别、工资、奖金、处罚记录)
- 开发一部有4个员工、开发二部有5名员工
- 分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer
- 分别统计出2个部门的平均月收入,要求去掉最高工资和最低工资
- 统计2个开发部整体的平均工资,去掉最低和最高工资的平均值
收集Stream流
public class SteamDemo04 {
public static double allMoney;
public static double allMoney2;
public static void main(String[] args) {
List<Employee> one = new ArrayList<>();
one.add(new Employee("猪八戒", '男', 30000, 22000, 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, 9000, "被打"));
two.add(new Employee("武大郎", '男', 20000, 9000, "下毒"));
//1.开发一部的最高员工的工资
//指定大小规则了
// Employee e = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e1.getSalary())).get();
// System.out.println("开发一部的最高工资员工是:" + e);
//
// Employee er = two.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary())).get();
// System.out.println("开发二部的最高工资员工是:" + er);
//封装为Topformer对象
Topperformer t = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary())).map(e
-> new Topperformer(e.getName(), e.getBonus() + e.getSalary())).get();
System.out.println("开发一部的最高工资人员为:" + t);
Topperformer t1 = two.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary())).
map(e -> new Topperformer(e.getName(), e.getBonus() + e.getSalary())).get();
System.out.println("开发二部最高工资人员为:" + t1);
//2.统计平均工资,去掉最高工资跟最低工资
one.stream().sorted((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(), e2.getBonus() + e2.getSalary()))
.skip(1).limit(one.size() - 2).forEach(e -> {
allMoney += (e.getBonus() + e.getSalary());
});
System.out.println("开发一部的平均工资为:" + allMoney / (one.size() - 2));
//统计两个部门整体的平均工资,去掉最高工资跟最低工资
//合并两个流
Stream<Employee> s1 = one.stream();
Stream<Employee> s2 = two.stream();
Stream<Employee> s3 = Stream.concat(s1, s2);
s3.sorted((e1, e2) ->Double.compare(e1.getBonus()+e1.getSalary(),e2.getBonus()+e2.getSalary()))
.skip(1).limit(one.size()-2).forEach(employee -> {
allMoney2+=(employee.getBonus()+employee.getSalary());
});
System.out.println("开发部的总工资为:"+allMoney2/(one.size()-2+two.size()));
}
}
output:
开发一部的最高工资人员为:Topperformer{name='猪八戒', money=52000.0}
开发二部最高工资人员为:Topperformer{name='西门庆', money=150000.0}
开发一部的平均工资为:42500.0
开发部的总工资为:7142.857142857143
Stream流的收集操作
- 收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去
- Stream流:方便操作集合/数组的手段。
- 集合/数组 才是开发中的目的
Stream流的收集方法
名称 | 说明 |
---|---|
R collect(Collector collector) | 开始收集Stream流,指定收集器 |
Collections 工具类提供了具体的收集方式
名称 | 说明 |
---|---|
public static |
把元素收集到List集合中 |
public static |
把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
public class StreamDemo05 {
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);
Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));
Set<String> zhangSet = s2.collect(Collectors.toSet());
System.out.println(zhangSet);
Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
String[] arr = s3.toArray(String[]::new);
System.out.println(Arrays.toString(arr));
// Object[] arrs = s3.toArray();
// System.out.println("arrs数组的内容是:"+ Arrays.toString(arrs));
}
}
output:
[张无忌, 张三丰, 张三]
[张三, 张三丰, 张无忌]
[张无忌, 张三丰, 张三]