Java8新特性(三)集合之 Stream 流式操作
程序员文章站
2022-06-12 10:30:11
stream就像一条大管套小管的管道,我们不用关心它的源头在哪里,只需要知道我们可以在目的地(海?)可以收集到来自五湖四海的一样。一般情况下,我们也不用管这管道源头在哪里,只需要拿到我们需要的东西。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。参考JDK8新特性(三):集合之 Stream 流式操作JDK8辅助学习(四):Strea....
stream就像一条大管套小管的管道,我们不用关心它的源头在哪里,只需要知道我们可以在目的地(海?)可以收集到来自五湖四海的一样。一般情况下,我们也不用管这管道源头在哪里,只需要拿到我们需要的东西。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
JDK8辅助学习(四):Stream流 collect() 方法的详细使用介绍
一、感受一下
Stream是什么?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算! ”
stream的特点:
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
需求:挑选出年龄大于12岁的学生,并按照年龄从大到小的顺序将它们排成一排。
package com._520xuzai.stream;
import java.util.List;
/**
* 学生对象
*/
public class Student {
private Integer id;
private int age;
private String name;
private List<String> courses;
public Student(int age, String name, List<String> courses) {
this.age = age;
this.name = name;
this.courses = courses;
this.id = 1;
}
public Student(Integer id, int age, String name, List<String> courses) {
this.id = id;
this.age = age;
this.name = name;
this.courses = courses;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getCourses() {
return courses;
}
public void setCourses(List<String> courses) {
this.courses = courses;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", courses=" + courses +
'}';
}
}
具体实现
package com._520xuzai.stream;
import org.junit.Test;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
private static List<Student> students = new ArrayList<>();
static {
for (int i=0;i<20;i++){
List<String> courses = new ArrayList<>();
int age = 5+i;
courses.add("语文");
courses.add("外语"+age);
courses.add("高数"+age);
students.add(new Student(age,"张三_"+age,courses));
}
}
//需求:挑选出年龄大于12岁的学生,并2.按照年龄从大到小的顺序将它们排成一排。
@Test
public void testFunction() {
//Java7(集合)实现
//使用迭代器筛选元素
List<Student> studentList = new ArrayList<Student>();
for (Student student : students) {
if (student.getAge() > 12) {
studentList.add(student);
}
}
//使用匿名类对学生进行年龄排序(从大到小)
Collections.sort(studentList, new Comparator<Student>() {
public int compare(Student s1, Student s2) {
return Integer.compare(s2.getAge(), s1.getAge());
}
});
//将排好序的学生排成一排(放到一起)
List<String> lastStudentList = new ArrayList<>();
for (Student s : studentList) {
lastStudentList.add(s.getName());
}
System.out.println("普通方式"+lastStudentList);
//Java8(Stream)实现
List<String> lastStudentList2 =
students.stream()
.filter(s -> s.getAge() > 12)//挑选出年龄大于12岁的学生
.sorted(Comparator.comparing(Student::getAge).reversed())//按照年龄从大到小(默认从小到大)进行排序
.map(Student::getName)//提取满足要求的学生的名字
.collect(Collectors.toList());//将名字保存到List中
System.out.println("stream的方式"+lastStudentList2);
}
}
二、Stream流常用方法
/**
* forEach() 方法用来遍历流中的数据,是一个终结方法。该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。
* @param action
*/
void forEach(Consumer<? super T> action);
/**
* count() 方法,用来统计集合中的元素个数,是一个终结方法。该方法返回一个 long 值代表元素个数
* @return
*/
long count();
/**
* filter() 方法,用于过滤数据,返回符合过滤条件的数据,是一个非终结方法。
* 我们可以通过 filter() 方法将一个流转换成另一个子集流。
* 该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或 方法引用) 作为筛选条件
* @param predicate
* @return
*/
Stream<T> filter(Predicate<? super T> predicate);
/**
* limit() 方法,用来对 Stream 流中的数据进行截取,只取用前 n 个,是一个非终结方法。
* 参数是一个 long 型,如果集合当前长度大于参数则进行截取,否则不进行操作。
* @param maxSize
* @return
*/
Stream<T> limit(long maxSize);
/**
* 如果希望跳过前几个元素,去取后面的元素,则可以使用 skip()方法,获取一个截取之后的新流
* @param n
* @return
*/
Stream<T> skip(long n);
/**
* map() 方法,可以将流中的元素映射到另一个流中。
* 即:可以将一种类型的流转换为另一种类型的流
* 可以将当前流中的T类型数据转换为另一种R类型的流
* @param mapper
* @param <R>
* @return
*/
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
/**
* flatMap 的使用,同 map 类似。
* map只是一维 1对1 的映射,而flatMap可以将一个2维的集合映射成一个一维,相当于它映射的深度比map深了一层。
* @param mapper
* @param <R>
* @return
*/
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
/**
* 根据元素的自然规律排序
* @return
*/
Stream<T> sorted();
/**
* 根据比较器指定的规则排序
* @param comparator
* @return
*/
Stream<T> sorted(Comparator<? super T> comparator);
/**
* distinct() 方法,可以用来去除重复数据
* @return
*/
Stream<T> distinct();
/**
* allMatch 全匹配(匹配所有,所有元素都需要满足条件-->返回true)
* @param predicate
* @return
*/
boolean allMatch(Predicate<? super T> predicate);
/**
* anyMatch 匹配某个元素(只要有一个元素满足条件即可-->返回true)
* @param predicate
* @return
*/
boolean anyMatch(Predicate<? super T> predicate);
/**
* noneMatch 匹配所有元素(所有元素都不满足指定条件-->返回true)
* @param predicate
* @return
*/
boolean noneMatch(Predicate<? super T> predicate);
/**
* findFirst() 、findAny() 方法,都是用来查找 Stream 流中的第一个元素。
* 返回值为 Optional<T>,意味着有可能找到也有可能找不到的情况
* @return
*/
Optional<T> findFirst();
Optional<T> findAny();
/**
* max() 和 min() 方法,用来获取 Stream 流中的最大值和最小值。
* 该接口需要一个 Comparator 函数式接口参数,根据指定排序规则来获取最大值,最小值。
*为了保证数据的准确性,此处排序规则需要是升序排序。
* 因为:max() 方法获取的是排序后的最后一个值,min() 方法获取的是排序后的第一个值。
* @param comparator
* @return
*/
Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);
三、实践
1、flatmap与map的区别
@Test
public void testFlatMap() {
// Stream<Student> studentStream = students.stream().filter(s -> (s.getAge() > 23 || s.getAge() > 23));
List<Student> studentList = new ArrayList<>();
List<String> courses1 = new ArrayList<>();
courses1.add("语文");
courses1.add("外语");
courses1.add("高数");
studentList.add(new Student(18,"张三",courses1));
courses1.clear();
courses1.add("语文");
courses1.add("物理");
courses1.add("化学");
studentList.add(new Student(19,"李四",courses1));
//使用map,需要多次forEach
Stream<List<String>> listStream = studentList.stream().map(student -> student.getCourses());
System.out.println("map1----------");
studentList.stream().map(student -> student.getCourses()).forEach(System.out::println);
System.out.println("map2----------");
studentList.stream().map(student -> student.getCourses().stream().map(s->s.replace("语文","政治"))).forEach(x -> System.out.println(Arrays.toString(x.toArray())));
System.out.println("map3----------");
studentList.stream().map(student->student.getCourses()).forEach(courses -> courses.forEach(System.out::println));
//使用 flatMap,一次forEach即可
Stream<String> stringStream = studentList.stream().flatMap(student -> student.getCourses().stream());
System.out.println("flatmap1----------");
studentList.stream().map(student->student.getCourses()).flatMap(student->student.stream()).forEach(System.out::println);
System.out.println("flatmap2----------");
studentList.stream().map(student->student.getCourses()).flatMap(student->student.stream().map(s -> s.replace("语文","政治"))).forEach(System.out::println);
System.out.println("flatmap3----------");
studentList.stream().flatMap(student->student.getCourses().stream()).forEach(System.out::println);
System.out.println("flatmap4----------");
studentList.stream().flatMap(student->student.getCourses().stream().map(s->s.replace("语文","政治"))).forEach(System.out::println);
}
输出结果:
map1----------
[语文, 物理, 化学]
[语文, 物理, 化学]
map2----------
[政治, 物理, 化学]
[政治, 物理, 化学]
map3----------
语文
物理
化学
语文
物理
化学
flatmap1----------
语文
物理
化学
语文
物理
化学
flatmap2----------
政治
物理
化学
政治
物理
化学
flatmap3----------
语文
物理
化学
语文
物理
化学
flatmap4----------
政治
物理
化学
政治
物理
化学
2、Collect相关操作
@Test
public void testCollect() {
// Stream<Student> studentStream = students.stream().filter(s -> (s.getAge() > 23 || s.getAge() > 23));
List<Student> studentList = new ArrayList<>();
List<String> courses1 = new ArrayList<>();
courses1.add("语文");
courses1.add("外语");
courses1.add("高数");
studentList.add(new Student(1,18, "张三", courses1));
courses1.clear();
courses1.add("语文");
courses1.add("物理");
courses1.add("化学");
studentList.add(new Student(2,18, "李四", courses1));
//转map,需要指定key和value,Function.identity()表示当前的Emp对象本身
studentList.stream().collect(Collectors.toMap(Student::getId,Function.identity())).forEach((x,y) -> System.out.println("id="+x+" __实体信息__"+y));
//分组操作
studentList.stream().collect(Collectors.groupingBy(Student::getAge)).forEach((x,y) -> System.out.println("age="+x+" __实体信息__"+y));
//分片
studentList.stream().collect(Collectors.partitioningBy(student -> student.getId() > 1)).forEach((x,y) -> System.out.println("根据id进行分区"+x+" __实体信息__"+y));
}
输出:
id=1 __实体信息__Student{age=18, name='张三', courses=[语文, 物理, 化学]}
id=2 __实体信息__Student{age=18, name='李四', courses=[语文, 物理, 化学]}
age=18 __实体信息__[Student{age=18, name='张三', courses=[语文, 物理, 化学]}, Student{age=18, name='李四', courses=[语文, 物理, 化学]}]
根据id进行分区false __实体信息__[Student{age=18, name='张三', courses=[语文, 物理, 化学]}]
根据id进行分区true __实体信息__[Student{age=18, name='李四', courses=[语文, 物理, 化学]}]
本文地址:https://blog.csdn.net/qq_39151461/article/details/112099902