Stream 接口

Stream 接口

在这里插入图片描述

一、概念

java.util.stream.Stream 接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选等操作。

​ Stream 接口借助于 Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。注:此接口和 IO 流没有任何联系。

二、使用步骤

  1. 创建 Stream,通过一个数据源来获取一个流。
  2. 转换 Stream,每次转换返回一个新的 Stream 对象。(因为中间操作的方法返回值仍为 Stream)
  3. 对 Stream 进行聚合操作并产生结果。(调用终止操作的方法)

三、创建方式:

  1. 方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
  2. 方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
  3. 方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
  4. 方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T> s)

大多使用第一种创建方式创建,即调用集合中的 stream() 方法。此处就不举例其他三种创建方式的例子了。

扩展:通配符的使用

​ 有时候我们希望传入的类型在一个指定的范围内,此时就可以使用泛型通配符了。泛型中有三种通配符形式:

  • <?> 无限制通配符:表示我们可以传入任意类型的参数。
  • <? extends E> 表示类型的上界是E,只能是 E 或者是 E 的子类。
  • <? super E> 表示类型的下界是E,只能是 E 或者是 E 的父类。

四、常用方法

1、中间操作

(1)筛选与切片
方法声明 功能介绍
Stream filter(Predicate<? super T> predicate) 返回一个包含匹配元素的流
Stream distinct() 返回不包含重复元素的流
Stream limit(long maxSize) 返回不超过给定元素数量的流
Stream skip(long n) 返回丢弃前n个元素后的流
//准备实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Comparable<Student>{
    
    
    private Integer id;
    private String name;
    private String grade;
    private Integer age;
}
//准备数据
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(1, "zhangfei", "大一", 25));
studentList.add(new Student(2, "guanyu", "大二", 27));
studentList.add(new Student(3, "liubei", "大二", 30));
studentList.add(new Student(4, "daqiao", "大一", 20));
studentList.add(new Student(5, "xiaoqiao", "大三", 18));
studentList.add(new Student(6, "zhouyu", "大三", 22));
studentList.add(new Student(7, "sunce", "大四", 23));
//将集合中年龄大于25岁的人过滤出来并放入另外一个集合中打印
studentList.stream().filter(student -> student.getAge() >= 25).forEach(System.out::println);

//limit()和skip()两个可以在分页的时候使用
PageDTO<StudentEntry> pageDTO = new PageDTO<>(pageNum, pageSize);
//根据页码和每页数量获取当前页数据
List<StudentEntry> pageList = new ArrayList<>();
if (!CollectionUtils.isEmpty(studentList)) {
    
    
    pageList = studentList.stream()
        .skip((long) (pageNum - 1) * pageSize)
        .limit(pageSize)
        .collect(Collectors.toList());
}
pageDTO.setResult(pageList);
//计算出页码总数赋值
long count = list.stream().count();
pageDTO.setTotalPages(pageDTO.pageCount(count));
pageDTO.setTotalRows(count);
(2)映射
方法声明 功能介绍
Stream map(Function<? super T,? extends R> mapper) 返回每个处理过元素组成的流
Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper) 返回每个被替换过元素组成的流,并将所有流合成一个流
//将集合中所有学生的名字拿出来并打印
studentList.stream().map(Student::getName).forEach(System.out::println);

//打印Helloworld
String[] words = new String[]{
    
    "Hello","World"};
List<String> list = Arrays.stream(words)
    .map(word -> word.split(""))
    .flatMap(Arrays::stream)
    .distinct()
    .collect(toList());
list.forEach(System.out::print);//Helloworld
(3)排序
方法声明 功能介绍
Stream sorted() 返回经过自然排序后元素组成的流
Stream sorted(Comparator<? super T> comparator) 返回经过比较器排序后元素组成的流
//实现集合中所有元素的自然排序并打印
studentList.stream().sorted().forEach(System.out::println);

2、终止操作

(1)匹配与查找
方法声明 功能介绍
Optional findFirst() 返回该流的第一个元素
boolean allMatch(Predicate<? super T> predicate) 返回所有元素是否匹配
boolean noneMatch(Predicate<? super T> predicate) 返回没有元素是否匹配
Optional max(Comparator<? super T> comparator) 根据比较器返回最大元素
Optional min(Comparator<? super T> comparator) 根据比较器返回最小元素
long count() 返回元素的个数
void forEach(Consumer<? super T> action) 对流中每个元素执行操作
//打印集合中的第一个元素
System.out.println(studentList.stream().findFirst());//Optional[Student{id=1, name='zhangfei', grade='大一', age=25}]
//判断集合中是否没有元素的年龄是大于35岁的
studentList.stream().noneMatch(student -> student.getAge() > 35);// true
//按照指定的比较器规则获取集合所有元素中的最大值
Optional<Student> max = studentList.stream().max(((o1, o2) -> o1.getAge() - o2.getAge()));
System.out.println(max);//Optional[Student{id=3, name='liubei', grade='大二', age=30}]
//count()和forEach()一直在用,就不再单独举例了
(2)收集(重要)
方法声明 功能介绍
<R,A> R collect(Collector<? super T,A,R> collector) 使用收集器对元素进行处理

collect() 方法是我们经常使用的方法,主要依赖 java.util.stream.Collectors 类内置的静态方法。下面将介绍几个常用的方法

  • toList()、toSet():将流中的数据经过处理后重新封装到一个新的集合中
// 1.将学生的年级放入集合
Set<String> set = studentList.stream().map(Student::getGrade).collect(Collectors.toSet());
//set.forEach(System.out::println);
System.out.println(set.toString());//[高四, 高三, 高二, 高一]

// 2.将学生名字放入集合
List<String> list = studentList.stream().map(Student::getName).collect(Collectors.toList());
System.out.println(list.toString());//[zhangfei, guanyu, liubei, daqiao, xiaoqiao, zhouyu, sunce]
  • toMap():将流中的数据经过处理后装到一个Map中,有三个重载的方法,分别为:

​ toMap(keyMapper, valueMapper)

​ toMap(keyMapper, valueMapper, mergeFunction)

扫描二维码关注公众号,回复: 15214593 查看本文章

​ toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)

​ 下面将分别举例来说明三个方法的使用场景:

// 3.将学生学号和姓名放入map,当出现相同key时,会抛异常
Map<Integer, String> map = studentList.stream().collect(Collectors.toMap(Student::getId, Student::getName));
System.out.println(map.toString());

// 4.将学生学号和班级放入map,当出现相同key时,用新值替换旧值
Map<Integer, String> map1 = studentList.stream().collect(Collectors.toMap(Student::getId, Student::getGrade, (oldId, newId) -> newId));
System.out.println(map1.toString());

// 5.将学生学号和班级放入自定义的HashMap
HashMap<Integer, String> hashMap = studentList.stream().collect(Collectors.toMap(Student::getId, Student::getGrade, (v1, v2) -> v2, HashMap::new));
System.out.println(hashMap.toString());

// 6.将学生学号和学生实体放入map
Map<Integer, Student> studentMap = studentList.stream().collect(Collectors.toMap(Student::getId, student -> student, (v1, v2) -> v2));
System.out.println(studentMap.toString());

// 7.根据年级进行分组,value为学生个数
Map<String, Integer> collect = studentList.stream().collect(Collectors.toMap(Student::getGrade, v -> 1, Integer::sum));
// Map<String, Integer> collect = studentList.stream().collect(Collectors.toMap(Student::getGrade, v -> 1, Integer::sum, HashMap::new));
System.out.println(collect.toString());//{大一=2, 大三=2, 大二=2, 大四=1}
  • joining():可以将流中的数据加上连接符,该方法也有三个重载的方法,分别为:

​ joining()

​ joining(CharSequence delimiter)

​ joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

//该方法一般用于字符串的拼接使用,比如前端展示的字段需要特定的字符串格式,而你数据库存的格式是单个存的格式,这时就可以将它放入集合中,然后通过该方法进行处理达到前端想要的效果
// 8.将学生的姓名打印出来,并中间加上~,如果该集合中只有一个元素,则不会添加~
String s = studentList.stream().map(Student::getName).collect(Collectors.joining("~"));
System.out.println(s);//zhangfei~guanyu~liubei~daqiao~xiaoqiao~zhouyu~sunce
// 9.将学生的姓名打印出来,并中间加上~,前面加上@,后面加上#
String s = studentList.stream().map(Student::getName).collect(Collectors.joining("~", "@", "#"));
System.out.println(s);//@zhangfei~guanyu~liubei~daqiao~xiaoqiao~zhouyu~sunce#
  • groupingBy():分组
//按照年级进行分组
System.out.println(studentList.stream().collect(Collectors.groupingBy(Student::getGender)));
//按照年级分组,打印每个人的id+age和
Map<String, Map<String, Integer>> collect = studentList.stream().collect(Collectors.groupingBy(
    Student::getGrade,
    Collectors.toMap(
        Student::getName,
        sum -> sum.getAge() + sum.getId())
));

collect.forEach((grade, idMap) -> {
    
    
    idMap.forEach((name, sum) -> {
    
    
        System.out.println("年级:" + grade + "姓名:" + name + "总和:" + sum);
    });
});
(3)规约
方法声明 功能介绍
Optional reduce(BinaryOperator accumulator) 返回结合后的元素值
//集合中所有元素的年龄映射出来并进行累加后打印
System.out.println(studentList.stream().map(Student::getAge).reduce((Integer::sum))); // 165

Optional 类

  • java.util.Optional 类可以理解为一个简单的容器,其值可能是 null 或者不是 null,代表一个值存在或不存在。
  • 该类的引入很好的解决空指针异常,不用显式进行空值检测。

常用方法:

方法声明 功能介绍
static Optional ofNullable(T value) 根据参数指定数值来得到Optional类型的对象
Optional map(Function<? super T,? extends U> mapper) 根据参数指定规则的结果来得到Optional类型的对象
T orElse(T other) 若该值存在就返回,否则返回other的数值
String str = null;
//System.out.println(str.length());//空指针异常

// Java8中使用Optional类实现空值的处理
// 1.将数据str装到Optional对象代表的容器中
Optional<String> optional = Optional.ofNullable(str);
// 2.建立映射关系  使用字符串的长度与字符串建立映射关系
Optional<Integer> integer = optional.map(String::length);
// 3.若字符串为空则打印0,否则打印字符串的数值
System.out.println(integer); // Optional.empty
System.out.println(integer.orElse(0)); // 0

猜你喜欢

转载自blog.csdn.net/m0_53067943/article/details/127921972
今日推荐