JDK的Stream API使用详解
构建流
所谓构建就是根据已有的对象构建出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 5s2.forEach(System.out::println);// 根据条件1s1 = Stream.of(1, 2, 3, 4, 5, 6);// 满足条件就dropStream<Integer> s3 = s1.dropWhile(i -> i % 2 == 0);s3.forEach(System.out::println);// 根据条件2s1 = Stream.of(1, 2, 3, 4, 5, 6);// 满足条件就takeStream<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,打印他们的nameusers.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的元素变成了StudentStream<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是一个ListMap<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相同的收集到ListCollectors.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的名字的长度小于等于4Stream<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的第一个参数用来生成一个新的streamStream<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最高的studentStream<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个,一旦调用了终止操作,前面的中间操作才真正开始执行。