Java Stream流

  • Stream API主要提供了两种类型的操作:中间操作终止操作
  • 中间操作是返回一个新的流,并在返回的流中包含所有之前的操作结果,总是延迟计算,这意味着它们只会在终止操作时执行,这样可以最大限度地优化资源使用
  • 终止操作返回一个结果或副作用(例如:显示控制台输出),并将流关闭

一、中间操作

1.Filter(过滤)

filter()方法接受一个谓词 (一个返回boolean值的函数),并返回一个 ,其中仅包含通过该谓词的元素

java 复制代码
例:建一个数组,筛选出长度大于4的元素

eg:
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");
        List<String> collect = names.stream().filter(item -> item.length() > 4).collect(Collectors.toList());
        System.out.println(collect);
    }
}
  • 这段代码创建了一个包含4个字符串的List集合,然后使用Stream()方法将其转化为一个Stream流
  • 接下来使用filter()方法筛选出长度大于4的字符串,返回一个新的包含符合条件元素的Stream流collect
  • 最后使用collect()方法将筛选后的结果转换成一个List集合
  • 使用Stream流中的filter()方法可以对流中的元素进行筛选过滤
  • 在这段代码中,lambda表达式item -> item.length() > 4指定了筛选判断条件,即只保留长度大于4的字符串
  • collect(Collectors.toList())则将筛选后的结果转换成一个List集合返回

2.Map(转换)

map()方法可将一个流的元素转换 为另一个流。它接受一个函数 ,该函数映射流中的每个元素另一个元素

java 复制代码
例:
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Map<Byte, Integer> collect = numbers.stream().collect(Collectors.toMap(Integer::byteValue, item -> item*2, (val1, val2) -> val2));
        for (Map.Entry<Byte, Integer> byteIntegerEntry : collect.entrySet()) {
            Byte key = byteIntegerEntry.getKey();
            System.out.println("key = " + key);
            System.out.println("Value = " + byteIntegerEntry.getValue());
        }
    }
}
  • 这段代码使用Java 8 中的 Stream API 实现了一种将数字列表 转换成字节-整数键值对的方式
  • 首先创建了一个包含数字 1~5 的列表,然后利用 stream() 方法将列表转换成 Stream 对象
  • 接下来调用 collect(Collectors.toMap(...)) 方法将 Stream 转换成Map<Byte, Integer>
  • 在 toMap 方法中,我们以每个整数的字节值为键,该整数乘以 2 为值,当遇到重复的键时取最后一个值。(这里实际上可以用任何能区分不同键的方式作为第一个参数,而不一定是 Integer::byteValue)
  • 最后,在 for 循环中遍历了这个 Map 并打印出每个键值对的内容

3.Sorted(排序)

sorted()方法可对流进行排序 。它可以接受一个Comparator 参数,也可以使用自然排序Ordering.natural()。默认排序是按升序排序

java 复制代码
public class Main {
    public static void main(String[] args) {
        int[] numbers = { 5, 2, 8, 3, 7 };
        int[] sortedNumbers = Arrays.stream(numbers).sorted().toArray();
        System.out.println(Arrays.toString(sortedNumbers));
    }
}
  • 这段代码创建了一个包含整数的数组numbers,然后使用Arrays.stream() 方法将其转化为一个IntStream
  • 接下来使用sorted()方法对流中的元素进行排序操作,返回一个新的排序后的IntStream流
  • 最后,使用toArray()方法将排序后的结果转换为一个新的int类型数组sortedNumbers,并使用Arrays.toString()方法将结果输出到控制台

4.Distinct(去重)

distinct()方法从流中返回所有不同 的元素。在内部,它使用**equals()**方法来比较元素是否相同。因此,我们需要确保equals()方法已正确实现

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 1);
        List<Integer> collect = numbers.stream().distinct().collect(Collectors.toList());
        System.out.println(collect);
    }
}
  • 这段代码创建了一个包含整数的List集合numbers,其中包含了若干个重复的整数
  • 接下来使用Stream()方法将其转化为一个Stream流
  • 使用**distinct()**方法对流中的元素进行去重操作,返回一个新的不包含重复元素的Stream流collect
  • 最后使用collect()方法将去重后的结果转换成一个List集合,并使用System.out.println()方法输出到控制台

5.Limit(限制)

limit()方法可以将流限制为指定的元素数

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> collect = numbers.stream().limit(3).collect(Collectors.toList());
        System.out.println(collect);
    }
}
  • 这段代码创建了一个包含整数的List集合numbers,其中包含了5个整数
  • 接下来使用Stream()方法将其转化为一个Stream流
  • 使用limit()方法对流中的元素进行限制操作,仅保留前3个元素,返回一个新的只包含前3个元素的Stream流collect
  • 最后使用collect()方法将限制操作后的结果转化为一个新的List集合,并使用System.out.println()方法输出到控制台

6.Skip(跳过)

skip()方法可跳过前N个元素

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> collect = numbers.stream().skip(2).collect(Collectors.toList());
        System.out.println(collect);
    }
}
  • 使用skip()方法对流中的元素进行跳过操作,跳过前2个元素,返回一个新的不包含前2个元素的Stream流collect。
  • 使用Stream流中的skip()方法可以快速地对集合中的元素进行跳过操作,跳过前N个元素。在这段代码中,集合中包含了5个整数,使用skip(2)方法跳过前2个元素,返回一个新的不包含前2个元素的List集合。
  • 运行该示例代码,输出结果为:[3, 4, 5],即不包含前2个元素的整数List集合。

7.Peek(展示)

peek()方法可以用于在Stream流中获取元素同时执行一些操作,如打印、调试、观察等。通常会与其他的方法联合使用。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");
        List<String> filteredNames = names.stream()
                .peek(System.out::println)
                .filter(name -> name.startsWith("C"))
                .peek(name -> System.out.println("Filtered value: " + name))
                .collect(Collectors.toList());
        System.out.println("-----------------------------------------------------------------");
        System.out.println(filteredNames);
    }
}

二、终止操作

1.forEach(循环)

forEach()方法可将给定的方法应用于流中的每个元素。该方法是一种消费流的方式,不会返回值

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");
        names.stream().forEach(System.out::println);
    }
}
  • 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作将每个元素打印到控制台
  • 具体来说,它使用 forEach() 方法遍历列表中的所有元素,并对每个元素执行打印操作其中,四个字符串元素按顺序打印到了控制台上
  • 注意到,使用 forEach() 方法时并没有指定任何条件或谓词,因此它会对列表中的所有元素进行操作,以达到遍历、打印等目的

2.Collect(收集)

collect()方法可以将流中的元素收集到一个集合中。一般与其他方法配合使用

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());
        System.out.println(evenNumbers);
    }
}
  • 这段代码创建了一个包含整数的列表 numbers,使用流式操作筛选出所有偶数,然后将它们收集到一个新的列表 evenNumbers 中,并打印输出
  • 具体来说,它使用了 filter() 方法过滤掉所有奇数元素,只保留所有偶数元素,并使用 collect() 方法将它们收集到一个新的列表 evenNumbers 中
  • 注意到,只有偶数元素被保留在了新列表 evenNumbers 中,而奇数元素全部被过滤掉了
  • 而且,在筛选偶数元素时,使用了 lambda 表达式 n -> n % 2 == 0,其中 % 表示取模操作,判断当前数是否为偶数
  • 如果 n % 2 的结果是 0,就把 n 这个数保留下来,否则就过滤掉

在 Java 的 Stream API 中,**Collectors**类提供了一系列静态方法,用于将流中的元素收集到各种数据结构中或进行汇总操作。

  • toList(): 将流中的元素收集到一个List
  • toSet(): 将流中的元素收集到一个Set中(去重)
  • toMap(...): 将流中的元素收集到一个Map
  • groupingBy(...): 根据指定条件对流中的元素进行分组,结果为Map<K, List<T>>
  • partitioningBy(...): 根据布尔条件将流分为两个组,结果为Map<Boolean, List<T>>
  • **counting():**统计流中元素的数量
  • **summingInt/Double/Long(...):**对流中元素的某个属性求和
  • **averagingInt/Double/Long(...):**对流中元素的某个属性求平均值
  • **maxBy/minBy(...):**根据比较器找出流中的最大值或最小值
  • joining(...): 将流中的字符串元素连接成一个字符串** **

3.Count(计数)

count()方法可以返回流中的元素数。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");
        long count = names.stream().count();
        System.out.println(count);
    }
}

4.Reduce(聚合)

reduce()方法可以将流元素聚合为单个结果。它接受一个BinaryOperator参数作为累加器。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);
        System.out.println(sum);
    }
}

5.AnyMatch(任意匹配)

anyMatch()方法如果至少有一个元素与给定的谓词匹配,则返回true。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");
        boolean anyStartsWithB = names.stream().anyMatch(name -> name.startsWith("B"));
        System.out.println(anyStartsWithB);
    }
}

6.AllMatch(全部匹配)

allMatch()方法如果所有元素都与给定谓词匹配,则返回true。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");
        boolean allStartsWithB = names.stream().allMatch(name -> name.startsWith("B"));
        System.out.println(allStartsWithB);
    }
}

7.NoneMatch(无匹配)

noneMatch()方法,如果没有任何元素与给定谓词匹配,则返回true。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");
        boolean noneStartsWithB = names.stream().noneMatch(name -> name.startsWith("E"));
        System.out.println(noneStartsWithB);
    }
}

三、总结

1.什么时候要用.stream()?

  • 在Java中,使用.stream()是为了将集合转换为Stream流,从而利用函数式编程特性(如过滤、映射、聚合等)
  • 当你需要对集合进行过滤(filter)、映射(map)、去重(distinct)、排序(sorted),使用聚合函数等操作时,必须转换为Stream
  • 不需要加 .stream() 的场景:直接遍历集合(forEach),直接访问集合属性
    size()get(index)contains()
    集合原生方法

四、实战

1.父子层级结构问题

java 复制代码
@ApiOperation(value = "学院-分页列表查询", notes = "学院-分页列表查询")
@GetMapping(value = "/list")
public Result<?> queryPageList(
        Colleges colleges,  // 接收查询条件的实体对象
        @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,  // 页码,默认为1
        @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,  // 每页数量,默认为10
        HttpServletRequest req  // HTTP请求对象,用于获取请求参数
) {
    // ============== 1. 构建查询条件并分页查询 ==============
    // 使用QueryGenerator根据请求参数和实体字段自动构建MyBatis-Plus查询条件
    QueryWrapper<Colleges> queryWrapper = QueryGenerator.initQueryWrapper(colleges, req.getParameterMap());
    // 添加排序条件:按sort字段升序排列
    queryWrapper.orderByAsc("sort");
    // 创建分页对象(当前页码,每页数量)
    Page<Colleges> page = new Page<>(pageNo, pageSize);
    // 执行分页查询,获取结果
    IPage<Colleges> pageList = collegesService.page(page, queryWrapper);

    // ============== 2. 收集父级学院ID ==============
    // 从分页结果中提取所有非空且非0的父学院ID(pid)
    List<Long> parentIds = pageList.getRecords().stream()
            .filter(item -> item.getPid() != null && item.getPid() != 0)  // 过滤有效pid
            .map(Colleges::getPid)  // 提取pid字段
            .distinct()  // 去重
            .collect(Collectors.toList());  // 转为List

    // ============== 3. 批量查询父级学院名称 ==============
    Map<Long, String> parentNameMap = new HashMap<>();  // 创建ID->名称的映射表
    if (!parentIds.isEmpty()) {
        // 批量查询父级学院实体
        collegesService.listByIds(parentIds).forEach(parent -> 
            // 将父学院ID和名称存入映射表
            parentNameMap.put(parent.getId(), parent.getName())
        );
    }

    // ============== 4. 设置父级学院名称 ==============
    pageList.getRecords().forEach(item -> {
        if (item.getPid() == null || item.getPid() == 0) {
            // 情况1:无父级(顶级学院)
            item.setParentname("顶级学院");
        } else {
            // 情况2:有父级
            // 从映射表中获取名称,若不存在则显示"未知学院"
            item.setParentname(parentNameMap.getOrDefault(item.getPid(), "未知学院"));
        }
    });

    // ============== 5. 返回结果 ==============
    // 将处理后的分页结果包装成功响应
    return Result.ok(pageList);
}
相关推荐
拾光Ծ2 小时前
【C++】STL之list模拟实现:关于链表容器的双向迭代器你知道多少?
开发语言·数据结构·c++·list·visual studio
数模加油站2 小时前
最新R(4.4.1)及R-studio保姆级安装配置详细教程及常见问题解答
开发语言·windows·数学建模·r语言
编程指南针2 小时前
2026新选题-基于Python的老年病医疗数据分析系统的设计与实现(数据采集+可视化分析)
开发语言·python·病历分析·医疗病历分析
学编程的小鬼2 小时前
全局异常处理器
java·spring boot
reasonsummer3 小时前
【办公类-116-01】20250929家长会PPT(Python快速批量制作16:9PPT相册,带文件名,照片横版和竖版)
java·数据库·python·powerpoint
暴力求解3 小时前
数据结构---栈和队列详解(上)
开发语言·数据结构·c++
ss2734 小时前
手写MyBatis第89弹:动态SQL解析与执行时机深度剖析
java·服务器·windows
froginwe114 小时前
Rust 基础语法
开发语言
hqwest4 小时前
QT肝8天15--左侧静态菜单
开发语言·数据库·qt·qt开发·ui控件