Java8 Stream 处理集合数据

今天给大家带来 Java 8 Stream 讲解,为什么直接讲这个,是因为只要你学完,立刻就能上手,并能让它在你的代码中大展身手。

值得注意的是:学习 Stream 之前必须先学习 lambda 的相关知识。本文也假设读者已经掌握 lambda 的相关知识

本篇文章主要内容:

  • 介绍 Stream 以及 Stream 是如何处理集合的
  • 介绍 Stream 与集合的关系与区别
  • Stream 的基本方法介绍

一. 什么是 Stream

Stream 中文称为 “流”,通过将集合转换为这么一种叫做 “流” 的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作。换句话说,你只需要告诉流你的要求和条件,流就会自行根据要求和条件对元素进行快速处理,从而得到你想要的结果

二. 流操作

其中数据源便是原始集合,然后将如 List<T> 的集合转换为 Stream<T> 类型的流,并对流进行一系列的中间操作,比如过滤保留部分元素、对元素进行排序、类型转换等;最后再进行一个终端操作,可以把 Stream 转换回集合类型,也可以直接对其中的各个元素进行处理,比如打印、比如计算总数、计算最大值等等

很重要的一点是,很多流操作本身就会返回一个流,所以多个操作可以直接连接起来,我们来看看一条 Stream 操作的代码:

四. 实战

首先创建一个 Student 对象

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Student implements Serializable {

    /**
     * 学成名字
     */
    private String name;

    /**
     * 性别
     */
    private String sex;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 成绩分数
     */
    private Integer grade;

    /**
     * 学科
     */
    private String subject;
}

并且设置相应的值和集合数据

 List<Student> studentList = new ArrayList<Student>(16) {{
            add(new Student("张三", "男", 18, 85.5, "数学"));
            add(new Student("李四", "男", 16, 92.5, "语文"));
            add(new Student("张珊", "女", 16, 89.0, "英语"));
            add(new Student("小明", "男", 15, 65.5, "数学"));
            add(new Student("小红", "女", 17, 74.5, "语文"));
 }};

1. stream() / parallelStream()

最常用到的方法,将集合转换为流

Stream<Student> studentListStream = studentList.stream();

而 parallelStream() 是并行流方法,能够让数据集执行并行操作

Stream<Student> studentListParallelStream = studentList.parallelStream();

2. filter()

保留 boolean 为 true 的元素

 // 过滤保留年龄大于17的学生
List<Student> students = studentList.stream().filter(s -> 17 < s.getAge()).collect(Collectors.toList());

打印输出:[Student(name=张三, sex=男, age=18, grade=85.5, subject=数学)]

3. distinct()

去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的

 // 过滤保留年龄大于17岁,去除相同名字的学生
List<String> students = studentList.stream().filter(s -> 17 == s.getAge()).map(Student::getName).distinct().collect(Collectors.toList());

打印输出: [小红]

4. sorted()

如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排序,需要自定义调用 sorted((T1, T2) -> return int) 实现 Comparator 接口

调用sorted()

// 年龄自然排序正序
List<Integer> students = studentList.stream().map(Student::getAge).sorted().collect(Collectors.toList());

打印输出:[15, 16, 16, 17, 17, 18]

调用sorted((T1, T2) -> return int)

// 年龄自然排序正序
List<Student> students = studentList.stream().sorted(Comparator.comparing(Student::getAge)).collect(Collectors.toList());

// 或者:
List<Student> students = studentList.stream().sorted((t1, t2)-> t1.getAge() - t2.getAge()).collect(Collectors.toList());

打印输出:[Student(name=小明, sex=男, age=15, grade=65.5, subject=数学), Student(name=李四, sex=男, age=16, grade=92.5, subject=语文), Student(name=张珊, sex=女, age=16, grade=89.0, subject=英语), Student(name=小红, sex=女, age=17, grade=74.5, subject=语文), Student(name=小红, sex=女, age=17, grade=71.5, subject=英语), Student(name=张三, sex=男, age=18, grade=85.5, subject=数学)]

5. limit()

截取返回前 n 个元素

// 截取返回前 2 个学生
List<Student> students = studentList.stream().limit(2).collect(Collectors.toList());

打印输出:[Student(name=张三, sex=男, age=18, grade=85.5, subject=数学), Student(name=李四, sex=男, age=16, grade=92.5, subject=语文)]

6. skip()

去除(跳过)前 n 个元素

List<Student> students = studentList.stream().skip(4).collect(Collectors.toList());

打印输出: [Student(name=小红, sex=女, age=17, grade=74.5, subject=语文), Student(name=小红, sex=女, age=17, grade=71.5, subject=英语)]

注意事项:

  • skip(m)用在 limit(n) 前面时,先去除前 m 个元素再返回剩余元素的前 n 个元素
  • limit(n) 用在 skip(m) 前面时,先返回前 n 个元素再在剩余的 n 个元素中去除 m 个元素
List<Student> students = studentList.stream().skip(2).limit(1).collect(Collectors.toList());

List<Student> students = studentList.stream().limit(2).skip(1).collect(Collectors.toList());

打印输出1: [Student(name=张珊, sex=女, age=16, grade=89.0, subject=英语)]
打印输出2: [Student(name=李四, sex=男, age=16, grade=92.5, subject=语文)]

7. map()

将流中的每一个元素 T 映射为 R(类似类型转换, 可以转换为任意类型)

// 输出人名
List<String> students = studentList.stream().map(Student::getName).collect(Collectors.toList());

// 将所有人的分数加5分
List<Double> students = studentList.stream().map(s -> s.getGrade() + 5.0D).collect(Collectors.toList());

打印输出1:[张三, 李四, 张珊, 小明, 小红, 小红]
打印输出2:[90.5, 97.5, 94.0, 70.5, 79.5, 76.5]

newlist 里面的元素为 list 中每一个 Person 对象的 name 变量

8. flatMap()

将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

List<String> students = studentList.stream().map(s-> s.getName().split("")).flatMap(Arrays::stream).collect(Collectors.toList());

打印输出:[张, 三, 李, 四, 张, 珊, 小, 明, 小, 红, 小, 红]

上面例子中,我们的目的是把 studentList 中每个人名字符串元素以" "分割开,变成一个新的 List<String>。
首先 map 方法分割每个字符串元素,但此时流的类型为 Stream<String[ ]>,因为 split 方法返回的是 String[ ] 类型;所以我们需要使用 flatMap 方法,先使用Arrays::stream将每个 String[ ] 元素变成一个 Stream<String> 流,然后 flatMap 会将每一个流连接成为一个流,最终返回我们需要的 Stream<String>

9. anyMatch()

流中是否有一个元素匹配给定的 T -> boolean 条件

// 是否存在一个student对象的年龄等于18:
boolean b = studentList.stream().anyMatch(s -> 18 == s.getAge());
打印输出:true

10. allMatch()

流中是否所有元素都匹配给定的 T -> boolean 条件

boolean b = studentList.stream().allMatch(s -> 18 == s.getAge());

打印输出: false

11. noneMatch()

流中是否没有元素匹配给定的 T -> boolean 条件

boolean b = studentList.stream().noneMatch(s -> 18 == s.getAge());

打印输出:false

12. findAny() 和 findFirst()

  • findAny():找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用 parallelStream() 并行时找到的是其中一个元素)
  • findFirst():找到第一个元素
Student student = studentList.stream().findFirst().get();
打印输出:Student(name=张三, sex=男, age=18, grade=85.5, subject=数学)

Student student = studentList.stream().findAny().get();
打印输出:Student(name=张三, sex=男, age=18, grade=85.5, subject=数学)

值得注意的是,这两个方法返回的是一个 Optional<T> 对象,它是一个容器类,能代表一个值存在或不存在,这个后面会讲到

14. reduce() 和 reduce()

用于组合流中的元素,如求和,求积,求最大值等

// 计算年龄总和:
Integer ages = studentList.stream().map(Student::getAge).reduce(Integer::sum).get();

// 或者:
Integer ages = studentList.stream().map(Student::getAge).reduce(0, Integer::sum);

其中,reduce 第一个参数 0 代表起始值为 0,lambda (a, b) -> a + b 即将两值相加产生一个新值:

// 计算年龄总乘积:
Integer ages = studentList.stream().map(Student::getAge).reduce(0, (t1, t2) -> t1 * t2);

15. count()

返回流中元素个数,结果为 long 类型

long count = studentList.stream().map(Student::getAge).count();

16. collect()

收集方法,我们很常用的是 collect(toList()),当然还有 collect(toSet()) 等,参数是一个收集器接口,这个后面会另外讲

List<Student> students = studentList.stream().unordered().collect(Collectors.toList());

17. forEach()

返回结果为 void,很明显我们可以通过它来干什么了,比方说:


// unordered() 还有这个比较不起眼的方法,返回一个等效的无序流,当然如果流本身就是无序的话,那可能就会直接返回其本身

// 打印各个元素:
studentList.forEach(System.out::println);

再比如说 MyBatis 里面访问数据库的 mapper 方法:

// 向数据库插入新元素:
studentList.stream().forEach(StudentMapper::insert);

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值