JDK的Stream API使用详解

发布于:2025-07-13 ⋅ 阅读:(18) ⋅ 点赞:(0)

构建流

所谓构建就是根据已有的对象构建出Stream流对象,主要有三种构建方式:

  • collection.stream() 从集合构建
  • Arrays.stream(数组) 从数组构建
  • Arrays.of(对象…) 从对象构建
public static void main(String[] args) {
    // 集合构建
    List<Integer> list = List.of(1, 2, 3);
    list.stream().forEach(System.out::println);
    Set<Integer> set = Set.of(1, 2, 3);
    set.stream().forEach(System.out::println);
    Map<Integer, String> map = Map.of(1, "a", 2, "b", 3, "c");
    map.entrySet().stream().forEach(System.out::println);
    // 数组构建
    int[] array= new int[]{1,2,3};
    Arrays.stream(array).forEach(System.out::println);
    // 对象构建
    Stream.of(1,2,3).forEach(System.out::println);
}

生成流

不利用已有的对象,而是利用某些规则去生成流中的元素。

  • IntStream.range() 根据范围生成
  • IntStream.iterate() 根据上一个元素生成下一个元素
  • IntStream.generate() 随机生成
public static void main(String[] args) {
    System.out.println("---------范围生成----------");
    // 含头不含尾
    IntStream s1 = IntStream.range(1, 10);
    s1.forEach(System.out::println);
    // 含头也含尾
    IntStream s2 = IntStream.rangeClosed(1, 10);
    s2.forEach(System.out::println);
    System.out.println("---------根据前一个生成后一个----------");
    IntStream s3 = IntStream.iterate(0, i -> i + 2).limit(5);
    s3.forEach(System.out::println);
    // 参数1:初始元素
    // 参数2:终止条件
    // 参数3:下一个元素的生成规则
    IntStream s4 = IntStream.iterate(0, i->i<10, i -> i + 2);
    s4.forEach(System.out::println);
    System.out.println("---------随机生成----------");
    IntStream s5 = IntStream.generate(() -> ThreadLocalRandom.current().nextInt(1, 100)).limit(5);
    s5.forEach(System.out::println);
}

合并流

把2个流合并成1个流

  • Stream.concat(stream1, stream2)
public static void main(String[] args) {
    Stream<Integer> s1 = Stream.of(1, 2, 3);
    Stream<Integer> s2 = Stream.of(4, 5, 6);
    Stream<Integer> s3 = Stream.concat(s1, s2);
    s3.forEach(System.out::println);
}

截取流

截取部分流

  • stream.offset(m).limit(n) 根据位置截取
  • stream.takeWhile(condition) 满足条件就留下
  • stream.dropWhile(condition) 满足条件就删除
public static void main(String[] args) {
    // 根据位置截取
    Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 跳过2个,取3个
    Stream<Integer> s2 = s1.skip(2).limit(3);
    // 输出3 4 5
    s2.forEach(System.out::println);
    // 根据条件1
    s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 满足条件就drop
    Stream<Integer> s3 = s1.dropWhile(i -> i % 2 == 0);
    s3.forEach(System.out::println);
    // 根据条件2
    s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 满足条件就take
    Stream<Integer> s4 = s1.takeWhile(i -> i % 2 == 0);
    s4.forEach(System.out::println);
}

注意:流只能遍历一次,就跟水从水管中流过一样,不能重复再从头遍历。

过滤

根据条件做筛选

  • stream.filter(过滤条件Predicate)
public static void main(String[] args) {
    Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 过滤出所有的偶数
    Stream<Integer> s2 = s1.filter(i -> i % 2 == 0);
    s2.forEach(System.out::println);
}

转换

根据规则做数据转换

  • stream.map(转换规则Function)
public record User(int id, String name, int age){}
public static void main(String[] args) {
    List<User> users = List.of(
    		new User(1, "tom", 15),
            new User(2, "jerry", 21),
            new User(3, "xjs", 22));
    // 挑选出age<20的user,打印他们的name
    users.stream()
            .filter(u->u.age < 20) //选出age<20
            .map(u->u.name)// user转化成name
            .forEach(System.out::println);
}

降维

降维就是从高维变成低维,比如从二维变成一维,又叫扁平化

  • stream.flatMap(返回一个stream的降维操作)

demo1:

public static void main(String[] args) {
    // 二维数组
    Integer arr[][] = {{1,2,3}, {4,5,6},{7,8,9}};
    // 流中的元素是一维数组
    Stream<Integer[]> s1 = Arrays.stream(arr);
    // 把每一个一维数组打散,新的流中每个元素就是Integer了
    Stream<Integer> s2 = s1.flatMap(a -> Arrays.stream(a));
    s2.forEach(System.out::println);
}

demo2:

public record Score(String subject, int score){}
public record Student(String name, int classId, List<Score> scores){}
public static void main(String[] args) {
    // classMap 中存放的是所有的班级的学生的成绩
    // key是班级的id,value是班级中的Studnet学生列表
    // 每个Studnet又有自己的名字和成绩Score列表
    // 须在需要:打印每一个学生的各科的成绩
    Map<Integer, List<Student>> classMap = Map.of(
            1, List.of(
                    new Student("zhangsan", 1, List.of(new Score("语文", 80), new Score("数学", 81))),
                    new Student("lisi", 1, List.of(new Score("语文", 90), new Score("数学", 91)))
                   ),
            2, List.of(
                    new Student("tom", 2, List.of(new Score("英语", 99), new Score("数学", 59))),
                    new Student("jerry", 2, List.of(new Score("英语", 100), new Score("语文", 59))))
    );
    // 拿到value的stream,每个value都是一个List<Student>
    Stream<List<Student>> s1 = classMap.values().stream();
    // 把List<Student>的元素打散放到一个新的stream中
    // steam的元素变成了Student
    Stream<Student> s2 = s1.flatMap(list -> list.stream());
    s2.forEach(System.out::println);
}

查找

  • stream.findAny() 查找任何一个符合条件的元素
  • stream.findFirst() 查找第一个符合条件的元素
  • stream.anyMatch(Predicate) 是否任何一个元素满足条件
  • stream.allMatch(Predicate) 是否所有的元素都满足条件
  • stream.noneMatch(Predicate) 是否所有的元素都不满足条件
public static void main(String[] args) {
    Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 查找任何一个符合条件的元素
    Integer i1 = s1.filter(i -> i % 2 == 0).findAny().orElse(-1);
    System.out.println("findAny:" + i1);
    s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 查找第一个符合条件的元素, 串行环境下,与findAny等价
    Integer i2 = s1.filter(i -> i % 2 == 0).findFirst().orElse(-1);
    System.out.println("findFirst:" + i2);
    s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 是否任何一个元素满足条件
    boolean anyMatch = s1.anyMatch(i -> i % 2 == 0);
    System.out.println("anyMatch:" + anyMatch);
    s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 是否所有的元素都满足条件
    boolean allMatch = s1.allMatch(i -> i % 2 == 0);
    System.out.println("allMatch:" + allMatch);
    s1 = Stream.of(1, 2, 3, 4, 5, 6);
    // 是否所有的元素都不满足条件
    boolean noneMatch = s1.noneMatch(i -> i % 2 == 0);
    System.out.println("noneMatch:" + noneMatch);
}

去重

stream.distinct()

public static void main(String[] args) {
    IntStream s1 = IntStream.of(1, 2, 3, 4, 5, 1, 2, 3, 4, 5);
    IntStream s2 = s1.distinct();
    s2.forEach(System.out::println);
}

排序

  • stream.sorted(Comparator) 按照比较器排序
  • Comparator.comparing(key提取Function)生成比较器
  • Comparator.thenComparing(key提取Function)进行二次比较
public record Score(String name, int score){}
public static Stream<Score> initStream(){
    return Stream.of(new Score("tom", 90),
            new Score("jerry", 85),
            new Score("jack", 92),
            new Score("rose", 77),
            new Score("xjs", 92));
}
public static void main(String[] args) {
    System.out.println("----按照score从小到大倒序输出-------");
    Stream<Score> s1 = initStream();
    s1.sorted((a, b) -> a.score - b.score).forEach(System.out::println);
    System.out.println("----按照score从大到小倒序输出-------");
    Stream<Score> s2 = initStream();
    s2.sorted((a, b) -> b.score - a.score).forEach(System.out::println);
    System.out.println("----调用Integer.compare方法-------");
    Stream<Score> s3 = initStream();
    s3.sorted((a, b) -> Integer.compare(a.score, b.score)).forEach(System.out::println);
    System.out.println("----调用Comparator.comparing方法-------");
    Stream<Score> s4 = initStream();
    s4.sorted(Comparator.comparing(Score::score)).forEach(System.out::println);
    System.out.println("----Comparator.reversed逆序输出-------");
    Stream<Score> s5 = initStream();
    s5.sorted(Comparator.comparingInt(Score::score).reversed()).forEach(System.out::println);
    System.out.println("----Comparator.thenComparing二次比较-------");
    Stream<Score> s6 = initStream();
    s6.sorted(Comparator.comparingInt(Score::score) // 先按照分数字
                        .thenComparing(a -> a.name)  //再按照名字的字母顺序比
    ).forEach(System.out::println);// rose jerry tom jack xjs
}

化简

两两合并最后只剩下一个,比如求和、求最大值、最小值、平均值等

  • stream.reduce(BinaryOperator)
  • stream.count() 求元素个数
  • stream.max(Comparator) 求最大值
  • stream.min(Comparator) 求最大值
  • IntStream.sum() 求和
  • IntStream.average()求平均值
  • stream.mapToInt(ToIntFunction)转化成IntStream
public static void main(String[] args) {
    System.out.println("----------reduce(BinaryOperator)--------");
    Stream<Integer> s1 = Stream.of(1, 3, 2, 6, 4, 5);
    // r:上次运算的结果,b当前的元素,本次计算完以后,会把计算结算结果当成下一个r
    // 注意返回结果是Optional,因为stream中可能一个元素也没有
    Optional<Integer> reduce = s1.reduce((r, b) -> r + b);
    System.out.println(reduce.orElse(null));
    System.out.println("----------reduce(init, BinaryOperator)--------");
    Stream<Integer> s2 = Stream.of(1, 3, 2, 6, 4, 5);
    // 第一个参数代表计算的初始结果
    // 注意返回结果, 因为有初始值,肯定有计算结果
    Integer r1 = s2.reduce(0, (r, b) -> r + b);
    System.out.println(r1);
    System.out.println("----------求最大值--------");
    Stream<Integer> s3 = Stream.of(1, 3, 2, 6, 4, 5);
    System.out.println(s3.reduce((a, b) -> Math.max(a, b)).orElse(Integer.MIN_VALUE));
    System.out.println("----------求元素个数--------");
    Stream<Integer> s4 = Stream.of(1, 3, 2, 6, 4, 5);
    System.out.println(s4.reduce(0, (r,a)->r+1));
    System.out.println("----------stream.count()求元素个数--------");
    Stream<Integer> s5 = Stream.of(1, 3, 2, 6, 4, 5);
    System.out.println(s5.count());
    System.out.println("----------stream.max(Comparator)求最大值--------");
    Stream<Integer> s6 = Stream.of(1, 3, 2, 6, 4, 5);
    Integer max = s6.max(Comparator.naturalOrder()).orElse(Integer.MIN_VALUE);
    System.out.println(max);
    System.out.println("----------IntStream.sum()求和--------");
    Stream<Integer> s7 = Stream.of(1, 3, 2, 6, 4, 5);
    int sum = s7.mapToInt(i -> i).sum();
    System.out.println(sum);
    System.out.println("----------IntStream.average()求平均值--------");
    Stream<Integer> s8 = Stream.of(1, 3, 2, 6, 4, 5);
    double average = s8.mapToInt(i -> i).average().orElse(0);
    System.out.println(average);
}

收集

把流中的元素收集到一个容器中去,比如:List、Set、Map、StringBuilder、StringJoiner等。

  • stream.collect(Supplier containerSupplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)) 收集
  • stream.collect(Collector) 使用Collector收集
  • Collectors.XXX() 生成Collector

普通收集:

public record Student(String name, int score){}
public static void main(String[] args) {
    System.out.println("-----收集到list------");
    Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);
    List<Integer> list = s1.collect(
            ArrayList::new,  // 生成收集到的目标容器
            ArrayList::add,  // 如何添加元素到容器
            (c1, c2)->{}); //并行流的时候,如何合并行执行的两个部分结果,暂时用不到
    System.out.println(list);
    System.out.println("-----收集到Map------");
    Stream<Student> s2 = Stream.of(new Student("tom", 93), new Student("jerry", 80), new Student("jack", 88));
    Map<String, Integer> map = s2.collect(HashMap::new, (m, s) -> m.put(s.name, s.score), (m1, m2) -> {});
    System.out.println(map);

    System.out.println("-----收集到StringJoiner------");
    Stream<Integer> s3 = Stream.of(1, 2, 3, 4, 5);
    StringJoiner s4 = s3.collect(() -> new StringJoiner(","), (c, e) -> c.add(""+e), (c1, c2) -> {});
    System.out.println(s4);
}

Collector收集:

public record Student(String name, int score){}
public static void main(String[] args) {
    System.out.println("-----收集到list------");
    Stream<Student> s1 = Stream.of(new Student("tom", 93), new Student("jerry", 80), new Student("jack", 88));
    List<Student> list = s1.collect(Collectors.toList());
    System.out.println(list);
    System.out.println("-----收集到StringJoiner------");
    Stream<Student> s2 = Stream.of(new Student("tom", 93), new Student("jerry", 80), new Student("jack", 88));
    String s3 = s2.map(Student::name).collect(Collectors.joining(","));
    System.out.println(s3);
    System.out.println("-----收集到map------");
    Stream<Student> s4 = Stream.of(new Student("tom", 93), new Student("jerry", 80), new Student("jack", 88));
    // 参数1:生成map的key 参数2:生成map的value 参数3:如果两个key是相同的怎么处理
    Map<String, Integer> map = s4.collect(Collectors.toMap(Student::name, Student::score, (k1, k2)->k1));
    System.out.println(map);
    System.out.println("-----收集到map------");
    // 生成Map<Integer, List<Student>>,比如:80分段的key是80, value是student列表
    Stream<Student> s5 = Stream.of(new Student("tom", 93), new Student("jerry", 80), new Student("jack", 88));
    // Collectors.groupingBy的第一个参数用来生成map的key,第二个参数用来生成map的value,也叫下游收集器,我们的value是一个List
    Map<Integer, List<Student>> group = s5.collect(Collectors.groupingBy(s -> s.score / 10 * 10, Collectors.toList()));
    // {80=[Student[name=jerry, score=80], Student[name=jack, score=88]], 90=[Student[name=tom, score=93]]}
    System.out.println(group);
}

分组相关-下游收集器1:

public record Student(String name, int score){}
public static void main(String[] args) {
    System.out.println("-----Collectors.mapping下游收集器------");
    // 生成Map<Integer, String>,比如:80分段的key是80, value是student的名字用,分割
    Stream<Student> s1 = initStream();
    Map<Integer, String> group = s1.collect(
            //Collectors.groupingBy的第一个参数是组的key, 第二个参数是组元素收集器,默认是key相同的收集到List
            Collectors.groupingBy(
                    s -> s.score / 10 * 10,
                            // Collectors.mapping的第一个参数对组(类型是List<Student>)中的元素如何做转换,第二个参数是转换以后的元素如何收集
                            Collectors.mapping(Student::name,
                                               Collectors.joining(",")
                            )));
    System.out.println(group);
    System.out.println("-----Collectors.filtering下游收集器------");
    // 生成Map<Integer, String>,比如:80分段的key是80, value是student的列表,student的名字的长度小于等于4
    Stream<Student> s2 = initStream();
    Map<Integer, List<Student>> group2 = s2.collect(Collectors.groupingBy(s -> s.score / 10 * 10, Collectors.filtering(s -> s.name.length() <= 4, Collectors.toList())));
    System.out.println(group2);
    System.out.println("-----Collectors.flatMapping下游收集器------");
    //Collectors.flatMapping的第一个参数用来生成一个新的stream
    Stream<Student> s3 = initStream();
    Map<Integer, List<String>> group3 = s3.collect(Collectors.groupingBy(s -> s.score / 10 * 10,
            Collectors.flatMapping(s-> Arrays.stream(s.name.split("")), Collectors.toList())));
    System.out.println(group3);
}
public static Stream<Student> initStream(){
    return  Stream.of(new Student("tom", 93),
            new Student("jerry", 80),
            new Student("jack", 88));
}

分组相关-下游收集器2:

public record Student(String name, int score){}
public static Stream<Student> initStream(){
    return  Stream.of(new Student("tom", 93),
            new Student("jerry", 90),
            new Student("jack", 80),
            new Student("rose", 85));
}
public static void main(String[] args) {
    System.out.println("-----Collectors.counting下游收集器------");
    // 生成Map<Integer, Integer>,比如:80分段的key是80, value是student的个数
    Stream<Student> s1 = initStream();
    Map<Integer, Long> group = s1.collect(Collectors.groupingBy(s -> s.score / 10 * 10, Collectors.counting()));
    System.out.println(group);
    System.out.println("-----Collectors.maxBy下游收集器------");
    // 生成Map<Integer, Student>,比如:80分段的key是80, value是组中score最高的student
    Stream<Student> s2 = initStream();
    Map<Integer, Optional<Student>> group2 = s2.collect(Collectors.groupingBy(s -> s.score / 10 * 10, Collectors.maxBy(Comparator.comparingInt(Student::score))));
    System.out.println(group2);
    System.out.println("-----Collectors.averagingInt下游收集器------");
    // 生成Map<Integer, Integer>,比如:80分段的key是80, value是组中score平均值
    Stream<Student> s3 = initStream();
    Map<Integer, Double> group3 = s3.collect(Collectors.groupingBy(s -> s.score / 10 * 10, Collectors.averagingInt(Student::score)));
    System.out.println(group3);
    System.out.println("-----Collectors.summingInt下游收集器------");
    // 生成Map<Integer, Integer>,比如:80分段的key是80, value是组中score之和
    Stream<Student> s4 = initStream();
    Map<Integer, Integer> group4 = s4.collect(Collectors.groupingBy(s -> s.score / 10 * 10, Collectors.summingInt(Student::score)));
    System.out.println(group4);
    System.out.println("-----Collectors.reducing下游收集器------");
    // 生成Map<Integer, Integer>,比如:80分段的key是80, value是组中score之和
    Stream<Student> s5 = initStream();
    Map<Integer, Integer> group5 = s5.collect(Collectors.groupingBy(s -> s.score / 10 * 10, Collectors.mapping(Student::score, Collectors.reducing(0, (r, e)-> r+e))));
    System.out.println(group5);
}

基本类型流

流的元素都是基本类型,而不是包装类型

  • IntStream
  • LongStream
  • DoubleStream

基本类型流里面会有一些普通流没有的额外操作,比如:

  • primitiveStream.sum() 求和
  • primitiveStream.min() 求最小值
  • primitiveStream.max() 求最大值
  • primitiveStream.average() 求平均值
  • primitiveStream.summaryStatistics() 统计汇总
  • primitiveStream.boxed() 转化成包装类型流
  • primitiveStream.mapToObj(IntFunction) 转化成其他类型的流
  • stream.mapToInt(ToIntFunction)其他类型的流转化成基本类型流
public static void main(String[] args) {
    System.out.println("------求和------");
    IntStream s1 = IntStream.of(1, 2, 3, 4, 5);
    int sum = s1.sum();
    System.out.println(sum);
    System.out.println("------统计汇总------");
    s1 = IntStream.of(1, 2, 3, 4, 5);
    IntSummaryStatistics intSummaryStatistics = s1.summaryStatistics();
    System.out.println(intSummaryStatistics);
    System.out.println("------转化成其他的流------");
    s1 = IntStream.of(1, 2, 3, 4, 5);
    Stream<String> s2 = s1.mapToObj(i -> "hello-" + i);
    s2.forEach(System.out::println);
    System.out.println("------mapToInt转化成基本类型流------");
    s2 = Stream.of("hello-1","hello-2","hello-3");
    IntStream s3 = s2.mapToInt(s -> Integer.parseInt(s.substring(6)));
    s3.forEach(System.out::println);
}

流的两种操作

一个流只能使用一次,不能重复使用。流的操作分2类:

  • 中间操作
    lazy延迟执行,可以有多个
  • 终止操作
    eager立即执行,只能有1个,一旦调用了终止操作,前面的中间操作才真正开始执行。

网站公告

今日签到

点亮在社区的每一天
去签到