Java 8 引入了 Stream API,它允许我们以声明式方式处理数据集合。Stream API 基于 Lambda 表达式,可以极大的提高我们的开发效率与代码可读性。

Stream 的特点:

  • 不会改变原有的数据源,会产生一个新Stream
  • 惰性求值:在终止操作时才会实际计算
  • 可消费性:流被消费后无法再次使用Stream 的构成:- Stream 的来源:集合、数组、I/O资源等
  • 中间操作:一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据筛选/变换,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性的,只有在最终的终止操作时才会真正启动计算。
  • 终止操作:一个流只能有一个终止操作,当这个操作执行后,流就被使用“消费”掉了,无法再被操作。所以这必定是流的最后一个操作。终止操作的执行,才会真正启动流水线,并产生结果。

常用的中间操作

  • filter: 过滤元素
  • map: 映射为其他形式或类型的元素
  • flatMap: 把 Stream 中的每个元素都换成另一个 Stream,然后把所有的 Stream 连接成一个大的 Stream
  • distinct: 过滤重复元素
  • sorted: 对流进行排序
  • limit: 截取流中的元素
  • skip: 跳过元素
  • peek: 对每个元素执行操作并返回一个新的 Stream

常用的终止操作

  • forEach: 为每个元素执行操作
  • collect: 收集流中的元素到集合中
  • count: 返回流中元素总数
  • min: 返回流中最小值
  • max: 返回流中最大值
  • anyMatch: 流中是否至少匹配一个元素
  • allMatch: 流中是否所有元素都匹配
  • noneMatch: 流中是否没有匹配的元素
  • findFirst: 返回第一个元素
  • findAny: 返回当前流中的任意元素

Stream 中间操作详解

filter: 过滤元素。filter 会生成一个排除了给定 predicate 不符合要求的元素的新流。

List<String> list = Arrays.asList("a", "b", "c", "d");
list.stream().filter(s -> s.equals("a"))
             .forEach(s -> System.out.println(s)); 
// a

map: 映射为其他形式或类型的元素。会将元素根据指定的 Function 接口来依次将其转换成另外一种形式,最终生成的是一个新的流,里面已经包含了所有的映射后的元素。

List<String> list = Arrays.asList("1", "2", "3");
list.stream().map(s -> Integer.parseInt(s))  
             .forEach(s -> System.out.println(s));
// 1 
// 2
// 3

flatMap: 把 Stream 中的每个元素都换成另一个 Stream,然后把所有的 Stream 连接成一个大的 Stream。主要用于 Stream 中的元素本身还是一个集合,我们需要把这个集合的元素提取出来,然后放到一个大的 Stream 里面。

List<List<String>> list = Arrays.asList(Arrays.asList("a"), Arrays.asList("b"));  
list.stream().flatMap(l -> l.stream())
             .forEach(s -> System.out.println(s));
// a
// b

distinct: 过滤重复元素。会检查当前元素和前面出现过的元素是否相等,如果不相等才会继续输出。

List<String> list = Arrays.asList("a", "b", "a", "c", "b");
list.stream().distinct()   
             .forEach(s -> System.out.println(s)); 
// a  
// b
// c

sorted: 对流进行排序。

List<String> list = Arrays.asList("c", "b", "a");
list.stream().sorted()  
             .forEach(s -> System.out.println(s));
// a   
// b
// c

limit: 截取流中的元素。可以对无限流进行截取,或者把一个流截取为更小的流。

List<String> list = Arrays.asList("a", "b", "c", "d"); 
list.stream().limit(2) 
             .forEach(s -> System.out.println(s));
// a
// b

skip: 跳过元素。可以像 limit 那样,对无限流进行操作,或者跳过流中的前 n 个元素。

List<String> list = Arrays.asList("a", "b", "c", "d");
list.stream().skip(2)  
             .forEach(s -> System.out.println(s));  
// c
// d

peek: 对每个元素执行操作并返回一个新的 Stream。主要用于调试,它允许我们在流水线的中间插入一个操作来观察流中的数据,而不影响操作的执行。

List<String> list = Arrays.asList("a", "b", "c");
list.stream().peek(s -> System.out.println(s)) 
             .filter(s -> s.equals("b")) 
             .forEach(s -> System.out.println(s));
// a 
// b
// b

Stream 终止操作详解

forEach: 为每个元素执行操作。这是最终的操作,遍历每个元素,执行给定的操作,不返回任何结果。

List<String> list = Arrays.asList("a", "b", "c");
list.stream().forEach(s -> System.out.println(s));
// a
// b 
// c

collect: 收集流中的元素到集合中。这是一个非常强大的终止操作,允许我们指定一个收集器来收集流中的元素到任意的结果集合中。

List<String> list = Arrays.asList("a", "b", "c");
List<String> result = list.stream().collect(Collectors.toList());
// [a, b, c]

count: 返回流中元素总数。这是一个非常简单的终止操作,返回流中元素的个数。

List<String> list = Arrays.asList("a", "b", "c");  
Long count = list.stream().count();
// 3

min: 返回流中最小值。

List<Integer> list = Arrays.asList(3, 1, 5, 2); 
Integer min = list.stream().min(Integer::compareTo).get();
// 1  

max: 返回流中最大值。

List<Integer> list = Arrays.asList(3, 1, 5, 2);  
Integer max = list.stream().max(Integer::compareTo).get();
// 5

anyMatch: 流中是否至少匹配一个元素。这是一个判断型终止操作,返回一个 boolean 值,表示流中是否至少有一个元素满足给定的 predicate。

List<String> list = Arrays.asList("a", "b", "c");
boolean anyMatchResult = list.stream().anyMatch(s -> s.equals("a"));  
// true

allMatch: 流中是否所有元素都匹配。这也是一个判断型终止操作,返回一个 boolean 值,表示流中是否所有元素都满足给定的 predicate。

List<String> list = Arrays.asList("a", "b", "c");
boolean allMatchResult = list.stream().allMatch(s -> s.length() > 0);  
// true  

noneMatch: 流中是否没有匹配的元素。这同样是一个判断型终止操作,表示流中是否没有元素满足给定的 predicate。

List<String> list = Arrays.asList("a", "b", "c");
boolean noneMatchResult = list.stream().noneMatch(s -> s.equals("d"));
// true

findFirst: 返回第一个元素。这是一个返回型终止操作,返回流中第一个元素。

List<String> list = Arrays.asList("a", "b", "c");  
String first = list.stream().findFirst().get();  
// a

findAny: 返回当前流中的任意元素。这也是一个返回型终止操作,返回流中的任意一个元素。

List<String> list = Arrays.asList("a", "b", "c");
String any = list.stream().findAny().get();
// a 或 b 或 c

练习题

stream流练习题,尽量使用stream流完成

package matsk.mszdqabbs;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;

public class TEST {
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Employee{
        private int id;
        private String name;
        private int salary;
        private String type;
    }
    static List<Employee> employees;

    public static void main(String[] args) {
        System.out.println(findDuplicateNameIgnoreCase());
    }

    static {
        employees = Arrays.asList(
                new Employee(1, "Tom", 8000, "IT"),
                new Employee(2, "Jack", 7000, "IT"),
                new Employee(3, "Lily", 9000, "HR"),
                new Employee(4, "Ann", 10000, "HR"),
                new Employee(5, "Bob", 6000, "IT"),
                new Employee(6, "Maria", 13000, "Finance"),
                new Employee(7, "Michael", 12000, "Finance"),
                new Employee(8, "Lisa", 11000, "HR"),
                new Employee(9, "Bill", 9500, "HR"),
                new Employee(10, "BILl", 9900, "IT"),
                new Employee(11, "BiLL", 9600, "IT")
        );
    }

    //1. 计算员工总数
    public static long totalCount() {
  
    }

    //2. 计算所有员工的工资总和
    public static int totalSalary() {
  
    }

    //3. 计算每个部门的平均工资
    public static Map<String, Double> avgSalaryByDept() {
  
    }

    //4. 找出最高工资的员工
    public static Employee highestPaid() {
  
    }

    //5. 找出工资低于8000的员工姓名
    public static List<String> lessThan8000() {
   
    }

    //6. 按部门对员工进行分组
    public static Map<String, List<Employee>> groupByDept() {
  
    }

    //7. 计算每个部门员工数量
    public static Map<String, Long> countByDept() {
  
    }

    //8.检查是否有员工的工资高于10000
    public static boolean anySalaryGreaterThan10000() {
  
    }

    //9. 对员工列表进行工资升序排列
    public static List<Employee> sortBySalaryAsc() {
  
    }

    //10. 对员工列表进行工资降序排列
    public static List<Employee> sortBySalaryDesc() {
  
    }

    //11. 将list转为以用户id为key,Employee为value的Map
    public static Map<Integer,Employee> toMap(){
  
    }

    //12. 将以用户id为key,Employee为value的Map转为List
    public static List<Employee> toList(){
  
    }

    //13. 根据员工名长度分组,得到Map<Integer, List<Employee>>
    public static Map<Integer, List<Employee>> groupByLength() {
  
    }

    //14. 检查员工中是否存在工资高于平均工资的人
    public static boolean anySalaryHigherThanAvg() {
  
    }

    //15. 将员工按工资从高到低排序,取前5名
    public static List<Employee> top5HighSalary() {
  
    }

    //16. 将员工按工资排序,取工资最高和第5高的人
    public static List<Employee> highestAndFifthSalary() {
  
    }

    //17.找出员工名中长度大于3的人
    public static List<Employee> nameLengthMoreThan3() {
  
    }


    //18. 检查Finance部门是否存在工资高于8000的员工
    public static boolean anyFinanceSalaryHigherThan8000() {
  
    }

    //19. 找出人员名重复的员工名(忽略大小写)
    public static List<String> findDuplicateNameIgnoreCase() {
  
    }
}

答案

public class TEST {
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Employee{
        private int id;
        private String name;
        private int salary;
        private String type;
    }
    static List<Employee> employees;

    public static void main(String[] args) {
        System.out.println(findDuplicateNameIgnoreCase());
    }

    static {
        employees = Arrays.asList(
                new Employee(1, "Tom", 8000, "IT"),
                new Employee(2, "Jack", 7000, "IT"),
                new Employee(3, "Lily", 9000, "HR"),
                new Employee(4, "Ann", 10000, "HR"),
                new Employee(5, "Bob", 6000, "IT"),
                new Employee(6, "Maria", 13000, "Finance"),
                new Employee(7, "Michael", 12000, "Finance"),
                new Employee(8, "Lisa", 11000, "HR"),
                new Employee(9, "Bill", 9500, "HR"),
                new Employee(10, "BILl", 9900, "IT"),
                new Employee(11, "BiLL", 9600, "IT")
        );
    }

    //1. 计算员工总数
    public static long totalCount() {
        return employees.stream().count();
    }

    //2. 计算所有员工的工资总和
    public static int totalSalary() {
        return employees.stream().mapToInt(Employee::getSalary).sum();
    }

    //3. 计算每个部门的平均工资
    public static Map<String, Double> avgSalaryByDept() {
        return employees.stream().collect(Collectors.groupingBy(Employee::getType,Collectors.averagingDouble(Employee::getSalary)));
    }

    //4. 找出最高工资的员工
    public static Employee highestPaid() {
        //return employees.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).findFirst().get();
        return employees.stream().max(Comparator.comparing(Employee::getSalary)).get();
    }

    //5. 找出工资低于8000的员工姓名
    public static List<String> lessThan8000() {
        return employees.stream().filter(e -> e.getSalary() < 8000).map(Employee::getName).collect(Collectors.toList());
    }

    //6. 按部门对员工进行分组
    public static Map<String, List<Employee>> groupByDept() {
        return employees.stream().collect(Collectors.groupingBy(Employee::getType));
    }

    //7. 计算每个部门员工数量
    public static Map<String, Long> countByDept() {
        return employees.stream().collect(Collectors.groupingBy(Employee::getType,Collectors.counting()));
    }

    //8.检查是否有员工的工资高于10000
    public static boolean anySalaryGreaterThan10000() {
        return employees.stream().anyMatch(e -> e.getSalary() > 10000);
    }

    //9. 对员工列表进行工资升序排列
    public static List<Employee> sortBySalaryAsc() {
        return employees.stream().sorted(Comparator.comparing(Employee::getSalary)).collect(Collectors.toList());
    }

    //10. 对员工列表进行工资降序排列
    public static List<Employee> sortBySalaryDesc() {
        return employees.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).collect(Collectors.toList());
    }

    //11. 将list转为以用户id为key,Employee为value的Map
    public static Map<Integer,Employee> toMap(){
        return employees.stream().collect(Collectors.toMap(Employee::getId,employee -> employee));
    }

    //12. 将以用户id为key,Employee为value的Map转为List
    public static List<Employee> toList(){
        Map<Integer, Employee> map = toMap();
        return  map.values().stream().collect(Collectors.toList());
//        return new ArrayList<>(map.values());
    }

    //13. 根据员工名长度分组,得到Map<Integer, List<Employee>>
    public static Map<Integer, List<Employee>> groupByLength() {
        return employees.stream().collect(Collectors.groupingBy(employee -> employee.getName().length()));
    }

    //14. 检查员工中是否存在工资高于平均工资的人
    public static boolean anySalaryHigherThanAvg() {
        Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        return employees.stream().anyMatch(e -> e.getSalary() > avg);
    }

    //15. 将员工按工资从高到低排序,取前5名
    public static List<Employee> top5HighSalary() {
        return employees
                .stream()
                .sorted(Comparator.comparing((Employee::getSalary))
                        .reversed()).limit(5).collect(Collectors.toList());
    }

    //16. 将员工按工资排序,取工资最高和第5高的人
    public static List<Employee> highestAndFifthSalary() {
        List<Employee> list = employees.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).collect(Collectors.toList());
        return Arrays.asList(list.get(0),list.get(4));
    }

    //17.找出员工名中长度大于3的人
    public static List<Employee> nameLengthMoreThan3() {
        return employees.stream().filter(e -> e.getName().length() > 3).collect(Collectors.toList());
    }


    //18. 检查Finance部门是否存在工资高于8000的员工
    public static boolean anyFinanceSalaryHigherThan8000() {
        return employees.stream().anyMatch(e -> e.getType().equals("Finance") && e.getSalary() > 8000);
    }

    //19. 找出人员名重复的员工名(忽略大小写)
    public static List<String> findDuplicateNameIgnoreCase() {
        Map<String, Long> map = employees.stream().peek(e -> e.setName(e.getName().toUpperCase())).collect(Collectors.groupingBy(Employee::getName, Collectors.counting()));
        return map.entrySet().stream().filter(entry -> entry.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toList());
    }
}
最后修改:2023 年 05 月 19 日
如果觉得我的文章对你有用,请随意赞赏