1.Stream流的叙述
在认识Stream流之前,我们先来看体会一下Stream流的作用
代码:
package com.itheima.d2_StreamDemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StreamDemo {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
Collections.addAll(names,"丹菲","大肥猪","丹菲猪");
// List<String> zhangList = new ArrayList<>();
// for (String name: names) {
// if (name.startsWith("丹"));
// {
// zhangList.add(name);
// }
// };
// System.out.println(zhangList);
//
// List<String> ZhangThreeName = new ArrayList<>();
// for (String ThreeName1:zhangList ) {
// if (ThreeName1.length() == 3)
// {
// ZhangThreeName.add(ThreeName1);
// }
// }
// System.out.println(ZhangThreeName);
//使用Stream实现
names.stream().filter(s -> s.startsWith("丹")).filter(s -> s.length() ==3 ).forEach(s -> System.out.println(s));
}
}
可以看出在学习Stream流之前,通过集合和数组的API来进行操作,还是相对比较麻烦的,但是引入了Sream流之后,操作就变的非常简便。
Stream流的思想:
Stream流的作用:简化数组和集合中的API操作。
2.Stream流的获取
Stream流的获取方式:
集合获取Stram流分为两种:Map集合获取Stream流和Collection集合获取Stream流,其中值得一提的是Map获取流的方式,分为三种 获取键的流,获取值的流,在之前的文章中讲过还有把键值对当成整体返回获取流。
代码:
package com.itheima.d2_StreamDemo;
import java.util.*;
import java.util.stream.Stream;
public class StreamDemo1 {
public static void main(String[] args) {
// -------------- Collection集合获取流
Collection<String> list = new ArrayList<>();
Stream<String> s = list.stream();
// Map集合获取流 三种 键 值 键值对
Map<String,Integer> map = new HashMap<>();
Stream<String> s1 = map.keySet().stream();
Stream<Integer> s2 = map.values().stream();
//键值对流 拿整体
Stream<Map.Entry<String,Integer>> s3 = map.entrySet().stream();
//数组获取流
String[] name = {"丹菲","丹菲猪"};
Stream<String> nameStream = Arrays.stream(name);
Stream<String> nameStream1 = Stream.of(name);
}
}
3.Stream流的常用API
Stream流的API操作就相当于对于数据的分析过程。 在Stream的常用API中分为两类方法,第一种是中间方法也就是在流上的操作,一次操作完毕后还可以继续执行其他操作,而这种方式我们也成为链式编程。 第二种是结束方法,当你使用该方法的时候就不能继续使用其他操作了,也就相当于结束了在流上的操作。
在这里讲一下filter方法的作用,filter方法相当于过滤的操作,将你想过滤数据的条件通过Lambda表达式写在括号内就可以,参数中写入该接口的实现类,也就是filter中操作的数据类型,只能与你流中的数据类型相同,在代码中是通过Lambda进行了简写。其中 System.out::println 也是简化形式,当你前后操作的变量相同的时候就可以简写成这个样子。
合并流方法注意点:如果两个流的数据类型不是同一数据类型的话,那么当你创建新的流去接收这个流的时候,新的流的数据类型需要是a和b的父类。
代码:
package com.itheima.d2_StreamDemo;
import com.itheima.d1_unchange_collection.Collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class StreamDemo2 {
//常用Api
/*
终结方法
forEach :逐一处理
count: 统计个数 -long count()
中间方法
filter
limit:取前几个元素
skip:跳过前几个
map: 加工
concat: 合并流
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰","张三丰");
// Stream<T> filter(Predicate<? super T> predicate);
// list.stream().filter(new Predicate<String>() {
// @Override
// public boolean test(String s) {
// return s.startsWith("张");
// }
// }).forEach(System.out::println);
//简写
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
long count = list.stream().filter(s -> s.length() == 3).count();
System.out.println(count);
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);
// Map加工方法
// 泛型前面为原材料类型 后面为结果类型
// list.stream().map(new Function<String, String>() {
// @Override
// public String apply(String s) {
// return "黑马的" + s;
// }
// });
list.stream().map(s -> "黑马的" + s).forEach(System.out::println);
// 把名称加工成对象
// list.stream().map(new Function<String, Student>() {
// @Override
// public Student apply(String s) {
// return new Student(s);
// }
// });
list.stream().map(s -> new Student(s)).forEach(System.out::println);// 构造器引用 方法引用
//合并流
// public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 源码拆解
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
Stream<Integer> s2 = Stream.of(22,23);
Stream<Object> s3 = Stream.concat(s1,s2);
//去除重复
s3.distinct().forEach(System.out::println);
}
}
过滤方法结果
包装成所需要字符串结果
包装成对象结果
去除重复结果
4.Stream流的综合应用
代码中用到了两个新的API public void max() 取最大值API和get()API获取当前操作类
在maxAPI中的代码原理其实就是通过实现类(匿名内部类)重写比较器接口中的比较规则来进行排序,可以是升序也可以是降序。想用对象来接收操作类时候,需要注意你在操作方法中返回的类 的类型需要与你对象类型一致。
在代码中静态成员变量的作用是在方法中接收数值,因为foreach方法跟main方法不在同一个方法栈,因此当你在main方法中创建变量的时候,你在该方法中是不可以用的,这个时候就可以用到静态成员变量进行接收,因为是随着类的加载而加载的。
代码:
package com.itheima.d2_StreamDemo;
import java.util.ArrayList;
import java.util.List;
public class StreamDemo3 {
public static int Allmoney = 0;
public static int Allmoney2 = 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,2000,null));
one.add(new Employee("小白龙","男",20000,25000,null));
List<Employee> two = new ArrayList<>();
two.add(new Employee("武松","男",30000,25000,null));
two.add(new Employee("李逵","男",25000,11000,"打老板"));
two.add(new Employee("西门庆","男",10000,12000,"偷情"));
two.add(new Employee("潘金莲","女",210000,125000,"下毒"));
//找到开发部最高工资员工
Topperformer tf = one.stream().max((e1,e2 )-> (e1.getSalary() + e1.getBonus())- (e2.getSalary() + e2.getBonus()))
.map(e -> new Topperformer(e.getName(),e.getSalary()+e.getBonus())).get();
System.out.println(tf);
//获取到两个部门的平均工资, 思想:先去除最高和最低工资的员工,拿到中间那批员工对象再去访问他们的属性。
one.stream().sorted((e1 ,e2) -> (e1.getSalary() +e1.getBonus()) - (e2.getBonus() + e2.getSalary()))
.skip(1).limit(one.size() -2 ).forEach(e ->{
Allmoney += (e.getBonus() +e.getSalary());
});
two.stream().sorted((e1 ,e2) -> (e1.getSalary() +e1.getBonus()) - (e2.getBonus() + e2.getSalary()))
.skip(1).limit(one.size() -2 ).forEach(e ->{
Allmoney2 += (e.getBonus() +e.getSalary());
});
System.out.println("开发二部平均工资" + Allmoney2 / (one.size() - 2) );
System.out.println("开发一部的平均工资是" + Allmoney / (one.size()-2 ) );
}
}
5.收集Stream流
为什么要收集Stream流呢? 因为Stream流他是一种对数据操作的过程,但是对于数据的接收我们平时基本上都是使用集合和数组进行接收,因此如果你想用对数据操作过程中的结果的话,就需要回收流,然后将结果存入数组或集合中。
Stream流在回收的时候分为两种API一种是返回集合一种是返回数组。
注意:在返回数组对象的时候,因为泛型不能规定流的数据类型,意思就是在可以通过特殊方法在流中加入其他类型的数据,因此接收数据的数组要为Object类。
需要注意的一点是,流只能操作一次,也就是说,当你创建的这个流已经回收过了,那么如果你再使用这个流进行操作的话,代码在运行的时候就会进行报错。
代码:
package com.itheima.d2_StreamDemo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamDemo4 {
/*
流的回收 因为流是一种分析数据的过程,
而我们平时接受数据是通过集合和数组 因此如果想用流中分析的结果的话,就需要用集合或者数组来回收流
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰","张三丰");
Stream<String> s = list.stream().filter(e -> e.startsWith("张"));
//通过Collectors返回List类型集合
List<String> list1 = s.collect(Collectors.toList());
System.out.println(list1);
//注意:流只能被用一次 如果想用的话需要再创建一个流。
//List<String> lis2 = s.collect(Collectors.toList());
//System.out.println(lis2);
Stream<String> s2 = list.stream().filter(e -> e.startsWith("张"));
//因为泛型不能规范流中的内容 可以通过其他方法往流中加数据 因此返回值类型要为OBJ
// Object[] arrs = s2.toArray();
//那么能不能返回成字符串数组呢?
//可以的 在参数中 new一个
String[] arrs= s2.toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});
System.out.println("数组内容 " + Arrays.toString(arrs));
}
}