Java8之Stream流
因为公司使用到了Stream流,所以学习了一手,以下是我的见解,欢迎交流学习。文章目录
- Java8之Stream流
- 一、什么是Stream流?
- 二、Stream流的常用方法
- 1.模拟Student实体类 创建Test类实现
- 2.Stream获取方式
- 3.Stream流实现方法
- 1.forEach() 循环
- 2.sorted() 单值排序与多值排序
- 3.groupingBy()单值分组与多值分组
- 4.filter():使用该方法拦截
- 5.limit():使用该方法截取
- 6.skip():与limit互斥,使用该方法跳过元素
- 7.distinct():使用该方法去重
- 8.max,min,sum,avg,count
- 9.map():接收一个方法作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- 10.flatMap():对每个元素执行mapper指定的操作,并用所有mapper返回的Stream中的元素组成一个新的Stream作为最终返回结果,通俗易懂就是将原来的stream中的所有元素都展开组成一个新的stream
- 11.使用 toList 接收处理完成的数据
- 三、使用Stream流的常见问题
- 练习题
一、什么是Stream流?
1、Stream的特性
- 对stream的操作可以分为两类,中间操作(intermediate operations)和结束操作(terminal operations)
- Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。
- 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
- Stream不保存数据,故每个Stream流只能使用一次。
2、与collections比较
- 无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
- 为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
- 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
- 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
二、Stream流的常用方法
1.模拟Student实体类 创建Test类实现
代码如下(示例):
1.实体类
/**
* @Author xhw
* @Date 2021/3/29 17:02
* @description: Student实体类
* @Version
*/
@Data
@AllArgsConstructor //所有参数构造
@NoArgsConstructor //无参构造
//@Data注解是Lombok插件的方法 用于实现SET GET
public class Student {
private String name;
private String sex;
private Integer age;
private String address;
}
2.测试类
/**
* @Author xhw
* @Date 2021/3/29 17:03
* @description:测试类
* @Version
*/
public class Test {
public static void main(String[] args) {
//创建测试集合
List<Student> list = Arrays.asList(
new Student("张三",18,"男","武汉"),
new Student("张三",18,"男","武汉"),
new Student("张一",19,"男","武汉"),
new Student("张二",20,"男","武汉"),
new Student("李四",20,"男","长沙"),
new Student("王五",22,"女","上海"),
new Student("赵六",24,"女","北京"),
new Student("周七",26,"男","广州"),
new Student("吴八",28,"女","武汉"),
new Student("正九",30,"男","武汉"),
new Student("王十",32,"女","武汉")
);
}
}
2.Stream获取方式
代码如下(示例):
// 1、数组
String[] arr = new String[]{"ab", "cd", "ef"};
Stream<String> arrStream = Arrays.stream(arr);
// 2、集合
List<String> newList = Arrays.asList("ab", "cd", "ef");
Stream<String> colStream = newList.stream();
// 3、值
Stream<String> stream = Stream.of("ab", "cd", "ef");
//4、Int数组转集合
int [] num = new int[]{1,2,3,4,5};
List<Integer> list1 = Arrays.stream(num).boxed().collect(Collectors.toList());
3.Stream流实现方法
1.forEach() 循环
// java 8 前
System.out.println("java 8 前");
for(Student stu: list){
System.out.println(stu);
}
// java 8 lambda
System.out.println("java 8 lambda");
list.forEach(stu -> System.out.println(stu));
// java 8 stream lambda
System.out.println("java 8 stream lambda");
list.stream().forEach(stu -> System.out.println(stu));
2.sorted() 单值排序与多值排序
// java 8 前
System.out.println("java 8 前");
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge().compareTo(o2.getAge());
}
});
for (Student stu : list) {
System.out.println(stu);
}
// java 8 stream 方法引用
System.out.println("java 8 stream 方法引用(正序)");
list.stream().sorted(Comparator.comparing(Student::getAge)).forEach(stu -> System.out.println(stu));
System.out.println("java 8 stream 方法引用(倒序)");
list.stream().sorted(Comparator.comparing(Student::getAge).reversed()).forEach(stu -> System.out.println(stu));
System.out.println("Stream流 多值排序(正序)");
list.stream().sorted(Comparator.comparing(Student::getAge)
.thenComparing(Student::getName)).forEach(stu -> System.out.println(stu));
System.out.println("Stream流 多值排序(倒序)");
list.stream().sorted(Comparator.comparing(Student::getAge,Comparator.reverseOrder())
.thenComparing(Student::getName,Comparator.reverseOrder())).forEach(stu -> System.out.println(stu));
3.groupingBy()单值分组与多值分组
List<Student> list = new ArrayList(); //DataSource
System.out.println("Stream流 单值分组");
//根据学生年龄进行分组
Map<String, List<Student>> studentMap =
list.parallelStream().collect(Collectors.groupingBy(Student::getAge));
System.out.println("Stream流 多值分组");
//根据班级、学生年龄进行分组 根据不同班级、不同年龄的学生给予特定编号
//因为入参为HashMao,为保证线程安全用AtomicReference转换迭代器
AtomicReference<Integer> groupId = new AtomicReference<>(0);
list.parallelStream()
.collect(Collectors.groupingBy(Student::getClassID, HashMap::new,
Collectors.groupingBy(Student::getAge))).forEach((k, v) -> {
//k => 班级编码
// v => 班级下所有学生信息 k1 => 年龄 v1 => 班级+年龄分组后 所有数据
v.forEach((k1, v1) -> {
//根据班级+年龄 分组ID迭代
groupId.getAndSet(groupId.get() + 1);
v1.forEach(s -> {
//特定编号
s.setStudentID(s.getClassID() + s.getAge() + "-" + groupId.get());
});
});
});
4.filter():使用该方法拦截
// 拦截小于24岁的人
System.out.println("-----过滤前-----");
list.forEach(stu-> System.out.println(stu));
System.out.println("-----过滤后-----");
// java 8 前
System.out.println("java 8 前");
for(Student stu: list){
if (stu.getAge() > 25) {
System.out.println(stu);
}
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().filter((Student stu) -> stu.getAge() >24).forEach(stu-> System.out.println(stu));
输出:
由上可见,此方法可以拦截24岁之前的Student,且包括24岁;
5.limit():使用该方法截取
// 从第一个开始截取,只取3个
System.out.println("-----截断前-----");
list.forEach(stu -> System.out.println(stu));
System.out.println("-----截断后-----");
// java 8 前
System.out.println("java 8 前");
for (int i = 0; i < 3; i++) {
System.out.println(list.get(i));
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().limit(3).forEach(stu -> System.out.println(stu));
输出:
6.skip():与limit互斥,使用该方法跳过元素
// 跳过前8个元素,从第9个开始输出
System.out.println("-----跳过前-----");
list.forEach(stu -> System.out.println(stu));
System.out.println("-----跳过后-----");
// java 8 前
System.out.println("java 8 前");
for (int i = 8; i < list.size(); i++) {
System.out.println(list.get(i));
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().skip(8).forEach(stu -> System.out.println(stu));
输出:
7.distinct():使用该方法去重
// 因为Arrays.asList() 返回的是Arrays的内部类ArrayList,操作remove,add会报错
// 为list去除重复数据
System.out.println("-----去重前-----");
list.forEach(stu -> System.out.println(stu));
System.out.println("-----去重后-----");
// java 8 前
System.out.println("java 8 前");
for (int i = 0; i < list.size() - 1; i++) {
for (int j = list.size() - 1; j > i; j--) {
if (list.get(j).getAge().equals(list.get(i).getAge()) && list.get(j).getName()
.equals(list.get(i).getName())) {
list.remove(i);
}
}
}
for (Student stu : list) {
System.out.println(stu);
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().distinct().forEach(stu -> System.out.println(stu));
注意:必须重写对应泛型的hashCode()和equals()方法,且参数一定要所有列对应,否则去重无效果。
输出:
8.max,min,sum,avg,count
IntSummaryStatistics num = list.stream().mapToInt(stu -> stu .getAge())
.summaryStatistics();
System.out.println("总共人数:" + num.getCount());
System.out.println("平均年龄:" + num.getAverage());
System.out.println("最大年龄:" + num.getMax());
System.out.println("最小年龄:" + num.getMin());
System.out.println("年龄之和:" + num.getSum());
输出:
9.map():接收一个方法作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
// 只输出所有人的年龄
list.stream().forEach(stu -> System.out.println(stu));
System.out.println("映射后----->");
List<Integer> ages = list.stream().map(stu -> stu.getAge()).collect(Collectors.toList());
ages.forEach(age -> System.out.println(age));
// 小写转大写
List<String> words = Arrays.asList("aaa", "vvvv", "cccc");
System.out.println("全部大写---->");
List<String> collect = words.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
collect.forEach(s -> System.out.println(s));
输出:
10.flatMap():对每个元素执行mapper指定的操作,并用所有mapper返回的Stream中的元素组成一个新的Stream作为最终返回结果,通俗易懂就是将原来的stream中的所有元素都展开组成一个新的stream
//创建一个 装有两个泛型为integer的集合
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5));
// 将两个合为一个
Stream<Integer> integerStream = stream.flatMap(
(Function<List<Integer>, Stream<Integer>>) integers -> integers.stream());
// 为新的集合
List<Integer> collect = integerStream.collect(toList());
System.out.println("新stream大小:"+collect.size());
System.out.println("-----合并后-----");
collect.forEach(o -> System.out.println(o));
输出:
11.使用 toList 接收处理完成的数据
List<String> list = new ArrayList<>();
list.add("1");
list.add("1");
list.add("2");
list.add("4");
list.add("4");
list.add("6");
List<String> collect = list.stream().distinct().collect(Collectors.toList());
System.out.println(list); // [1, 1, 2, 4, 4, 6]
System.out.println(collect); // [1, 2, 4, 6]
三、使用Stream流的常见问题
1.使用sorted()排序后groupingBy()进行分组导致排序失效
List<Student> list = new ArrayList(); //DataSource
//先进行排序后 入参为LinkedHashMap 变为有序集合 避免排序混乱
Map<String, List<Student>> collect =
list.stream()
.sorted(Comparator.comparing(Student::getClassID)
.thenComparing(Student::getAge)
.thenComparing(Student::getName)
.thenComparing(Student::getId))
.collect(Collectors.groupingBy
(Student::getClassId,LinkedHashMap::new, Collectors.toList()));
练习题
- 找到不是武汉年龄小于25岁的男生,去重后根据年龄顺序,取前三
list.stream()
.distinct()
.filter(stu ->stu.getAge() <25)
.filter(stu ->!stu.getAddress().equals("武汉"))
.sorted(Comparator.comparing(Student::getAge))
.limit(3).forEach(stu -> System.out.println(stu));
- 去重后计算出 list总记录数 age的平均 最大 最小值 以及年龄总和
//去重后转换成新数组进行接收
List<Student> newList =
list.stream().distinct().collect(Collectors.toList());
IntSummaryStatistics num = newList.stream().mapToInt(s -> s.getAge())
.summaryStatistics();
System.out.println("长度:"+newList.size());
System.out.println("最大年龄:" + num.getMax());
System.out.println("最小年龄:" + num.getMin());
System.out.println("年龄之和:" + num.getSum());
System.out.println("平均年龄:" + num.getAverage());
- 给你一个有序数组 nums,请你删除重复出现的元素,使每个元素只出现一次且倒序输出,最后返回删除后数组的新长度
public static int removeDuplicates(int[] nums){
List<Integer> list =
//将数组转为List进行stream流操作
Arrays.stream(nums).boxed().collect(Collectors.toList());
List<Integer> newList
//去重 排序倒序 转成集合储存
=list.stream().distinct().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
//输出结果
newList.stream().forEach(plt -> System.out.println(plt));
}
public static void main(String[] args) {
int [] nums = {1,1,1,2,2,3,4,5};
int i = removeDuplicates(nums);
System.out.println(i);
}