目录

Java-Stream-API-的使用

目录

Java Stream API 的使用

java8引入的java.util.stream.Stream流操作,使得访问和操作数组(Array)、集合(Collection)变得非常方便和优雅。

1、过滤元素和转化元素类型

    private static void filterMapToInt() {
        List<String> list = new ArrayList<>();
        list.add("12");
        list.add("ab");
        list.add("34");

        int[] array = list.stream().filter(str -> {
            Pattern pattern = Pattern.compile("\\d+");
            return pattern.matcher(str).find();
        }).mapToInt(Integer::parseInt).toArray();
        System.out.println(Arrays.toString(array)); // [12, 34]
    }

2、平铺流元素和转化

    private static void flatMapStreamOfMap() {
        List<String> list = new ArrayList<>();
        list.add("12,ab,34");
        list.add("11,22,bb");
        Stream<String> stream = list.stream();
        Stream<String> stringStream = stream.flatMap(str -> Stream.of(str.split(",")).map(String::toUpperCase));
        System.out.println(stringStream.toList()); // [12, AB, 34, 11, 22, BB]
    }

3、嵌套类型转为列表

private static void mapMulti() {
        List<Object> objects = List.of(1, List.of(2, List.of(3, 4)), 5);
        Stream<Object> objectStream = objects.stream().mapMulti(StreamTest::expandIterable);
        List<Integer> list = objectStream.map(o -> Integer.parseInt(o.toString())).toList();
        System.out.println(list); // [1, 2, 3, 4, 5]
    }

    static void expandIterable(Object e, Consumer<Object> c) {
        if (e instanceof Iterable<?> elements) {
            for (Object ie : elements) {
                expandIterable(ie, c);
            }
        } else if (e != null) {
            c.accept(e);
        }
    }

4、多个数据类型转为单一类型

private static void mapMulti2() {
        Map<String, Object> map = new HashMap<>();
        map.put("aa", new Object());
        map.put("23.3", new Object());
        List<Object> objects = List.of(1, map, 1.345f, List.of("bb", 2.45d));
        // 整数不变,浮点数转化为整数,字符串丢弃,map只需要key
        Stream<Object> objectStream = objects.stream().mapMulti(StreamTest::handleMapMulti);
        System.out.println(objectStream.map(object -> Integer.parseInt(object.toString())).toList()); // [1, 23, 1, 2]
    }

    static void handleMapMulti(Object e, Consumer<Object> c) {
        if (e instanceof Iterable<?> elements) {
            for (Object ie : elements) {
                handleMapMulti(ie, c);
            }
        } else if (e != null) {
            Object intTarget = e;
            if (e instanceof Map<?, ?> map) {
                handleMapMulti(map.keySet(), c);
            }
            if (intTarget instanceof Float f) {
                intTarget = Math.round(f);
            }
            if (intTarget instanceof Double d) {
                intTarget = (int) Math.round(d);
            }
            if (intTarget instanceof String str) {
                try {
                    intTarget = (int) Float.parseFloat(str);
                } catch (NumberFormatException _) {
                }
            }
            if (intTarget instanceof Integer integer) {
                c.accept(integer);
            }
        }
    }

5、元素去重

    private static void distinct() {
        List<String> list = new ArrayList<>();
        list.add("aa");
        list.add("aa");
        list.add("bb");
        System.out.println(list.stream().distinct().toList()); // [aa, bb]
    }

6、无参数排序(元素自己实现了Comparable)

    private static void sortedByItself() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(3);
        list.add(2);
        System.out.println(list.stream().sorted().toList()); // [1, 2, 3]
        System.out.println(list.stream().sorted(Comparator.reverseOrder()).toList()); // [3, 2, 1] 倒序
    }

7、有参数排序(传入Comparator)

    private static void sortedByComparator() {
        List<String> list = new ArrayList<>();
        list.add("xiaoming,96");
        list.add("xiaohong,80");
        list.add("xiaozhang,100");
        List<String> list1 = list.stream().sorted((s1, s2) -> {
            String[] info1 = s1.split(",");
            String[] info2 = s2.split(",");
            int score1 = Integer.parseInt(info1[1]);
            int score2 = Integer.parseInt(info2[1]);
            return score2 - score1;
        }).toList();
        System.out.println(list1); // [xiaozhang,100, xiaoming,96, xiaohong,80]
    }

8、用于debug程序的peek

    private static void debugByPeek() {
        List<String> list = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e)).toList();
        System.out.println(list);
    }

打印:

Filtered value: three

Mapped value: THREE

Filtered value: four

Mapped value: FOUR

[THREE, FOUR]

9、限制返回个数

    private static void limit() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(3);
        list.add(2);
        System.out.println(list.stream().limit(2).toList()); // [1, 3]
    }

10、跳过若干个元素

    private static void skip() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(3);
        list.add(2);
        System.out.println(list.stream().skip(2).toList()); // [2]
    }

11、有条件的获取元素(java9)

    private static void taskWhile() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 使用 takeWhile 提取元素,直到遇到第一个大于 5 的元素
        Stream<Integer> takenStream = stream.takeWhile(n -> n <= 5);
        // 输出提取的元素
        takenStream.forEach(System.out::print); // 12345
    }

12、有条件的丢弃元素(java9)

    private static void dropWhile() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 使用 dropWhile 提取元素,丢弃小于等于 5 的元素
        Stream<Integer> takenStream = stream.dropWhile(n -> n <= 5);
        // 输出提取的元素
        takenStream.forEach(System.out::print); // 678910
    }

13、按顺序执行(并行流(多线程)也能按顺序)

    private static void foreachOrdered() {
        // 在并行流中,forEachOrdered 需要额外的同步机制来保证顺序,因此性能可能不如 forEach。如果顺序不重要,优先使用 forEach
        // 如果流本身是无序的(例如 HashSet 的流),forEachOrdered 的行为可能与 forEach 相同
        // 在串行流中,forEach 和 forEachOrdered 的行为几乎一致
        Stream<Integer> parallelStream = Stream.of(1, 2, 3, 4, 5).parallel();
        parallelStream.forEach(System.out::print); // 34152 顺序不保证
        Stream<Integer> parallelStream2 = Stream.of(1, 2, 3, 4, 5).parallel();
        parallelStream2.forEachOrdered(System.out::print); // 12345 顺序保证
    }

14、转为list或者转为数组

    private static void toListToArray() {
        String s = "1,2,3";
        List<String> list = Stream.of(s.split(",")).toList();
        Object[] objectArr = Stream.of(s.split(",")).toArray();
        String[] strArr = Stream.of(s.split(",")).toArray(String[]::new);
        Integer[] intArr = Stream.of(s.split(",")).map(Integer::parseInt).toArray(Integer[]::new);
        System.out.println("stream.toList:" + list);
        System.out.println("stream.toArray:" + Arrays.toString(objectArr));
        System.out.println("stream.toArray(String[]::new):" + Arrays.toString(strArr));
        System.out.println("stream.toArray(Integer[]::new):" + Arrays.toString(intArr));
    }

打印:

stream.toList:[1, 2, 3]

stream.toArray:[1, 2, 3]

stream.toArray(String[]::new):[1, 2, 3]

stream.toArray(Integer[]::new):[1, 2, 3]

15、将列表转个单个元素(累积函数,根据某个规则将多个元素合并)

    private static void reduce() {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        Optional<Integer> sum = numbers.stream().reduce(Integer::sum); // 求和
        System.out.println(sum.orElse(0)); // 输出 6
        Optional<Integer> max = numbers.stream().reduce(Integer::max);// 最大值
        System.out.println(max.orElse(0)); // 输出 3
        List<String> list = Arrays.asList("Hello ", "world!");
        String concat = list.stream().reduce("", String::concat); // 字符调拼接
        System.out.println(concat); // Hello world!

        // 初始值(identity)
        // 必须是累积函数的单位元,即满足:
        // accumulator.apply(identity, t) == t
        // 例如:加法初始值为0(0 + t = t),乘法初始值为1(1 * t = t)
        List<Integer> intList = Arrays.asList(1, 2, 3);
        int product = intList.stream().reduce(1, (a, b) -> a * b);
        System.out.println(product); // 输出 6
    }

16、把流转化为集合、Set或者对流分组和分区

  private static void collect() {
        // 转为 List (等价于:Stream.of("1", "2", "3").toList())
        List<String> list = Stream.of("1", "2", "3").collect(Collectors.toList());
        // 转为 Set(自动去重)
        Set<String> set = Stream.of("1", "2", "3").collect(Collectors.toSet());
        // 转为特定集合类型
        TreeSet<String> treeSet = Stream.of("1", "2", "3").collect(Collectors.toCollection(TreeSet::new));
        // 直接拼接 (等价于: String.join("", "1", "2", "3");)
        String joined = Stream.of("1", "2", "3").collect(Collectors.joining());
        // 带分隔符(等价于:String.join(", ", "1", "2", "3");)
        String joinedWithComma = Stream.of("1", "2", "3").collect(Collectors.joining(", "));
        // 带前缀和后缀
        String wrapped = Stream.of("1", "2", "3").collect(Collectors.joining(", ", "[", "]"));
        // 平均值
        Double avg = Stream.of("1", "2", "3").collect(Collectors.averagingInt(Integer::parseInt));
        // 总和 (等价于:Stream.of("1", "2", "3").mapToInt(Integer::parseInt).sum();)
        Integer sum = Stream.of("1", "2", "3").collect(Collectors.summingInt(Integer::parseInt));

        // 基础用法(键重复会抛出异常)
        Map<String, Integer> map = Stream.of(new Student(93, "xiaoming")).collect(
                Collectors.toMap(
                        Student::getName,  // 键提取函数
                        Student::getScore // 值提取函数
                )
        );

        // 处理键冲突(第三个参数定义合并策略)
        Map<String, Integer> safeMap = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaohong"), new Student(6, "xiaohong")).collect(
                Collectors.toMap(
                        Student::getName,
                        Student::getScore,
                        Integer::sum // 合并重复键的值
                )
        );

        // 自定义 Map 类型(如 LinkedHashMap)
        Map<String, Integer> linkedMap = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaoming")).collect(
                Collectors.toMap(
                        Student::getName,
                        Student::getScore,
                        (oldVal, _) -> oldVal,
                        LinkedHashMap::new
                )
        );

        // 示例:自定义收集到逗号分隔的字符串
        Collector<Student, StringJoiner, String> customCollector =
                Collector.of(
                        () -> new StringJoiner(","), // Supplier(初始化容器)
                        (j, e) -> j.add(e.getName()), // Accumulator(累加器:添加元素)
                        StringJoiner::merge,          // Combiner(多线程:合并并行结果)
                        StringJoiner::toString        // Finisher(最终转换)
                );

        String names = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaohong"))
                .collect(customCollector);
        System.out.println(names); // xiaoming, xiaohong

        // 分区(按布尔条件分为两组)
        Map<Boolean, List<Student>> partition = Stream.of(new Student(93, "xiaoming"), new Student(94, "xiaohong"))
                .collect(Collectors.partitioningBy(e -> e.getScore() > 93));
        System.out.println(partition);  // {false=[score=93, name='xiaoming], true=[score=94, name='xiaohong]}

        // 按学科分组(默认返回 Map<K, List<T>>)
        Map<String, List<Student>> groupByDept = Stream.of(new Student(93, "xiaoming", "English"))
                .collect(Collectors.groupingBy(Student::getSubject));

        // 分组后统计(下游收集器,统计每个学科有多少人)
        Map<String, Long> countByDept = Stream.of(new Student(93, "xiaoming", "English"))
                .collect(Collectors.groupingBy(
                        Student::getSubject,
                        Collectors.counting() // 统计每组的元素数量
                ));
    }

    static class Student {
        int score;
        String name;
        String subject;
    }

代码中Student省略getter、setter和构造函数

17、求最大值最小值

    private static void minMax() {
        Optional<Integer> min = Stream.of(1, 2, 3).min(Integer::compareTo);
        System.out.println(min.orElse(0)); // 1
        Optional<Integer> max = Stream.of(1, 2, 3).max(Integer::compareTo);
        System.out.println(max.orElse(4)); // 3
    }

18、计算流包含的元素个数

    private static void count() {
        System.out.println(Stream.of(1,2,3).count()); // 3
    }

19、执行条件匹配的判断

    private static void anyMatchAllMatchNoneMatch() {
        System.out.println(Stream.of(1, 2, 3).anyMatch(num -> num > 1));  // true
        System.out.println(Stream.of(1, 2, 3).allMatch(num -> num > 2));  // false,不是全都大于2
        System.out.println(Stream.of(1, 2, 3).noneMatch(num -> num > 1)); // false,存在匹配的数据
    }

20、查找流的第一个元素或者任意一个元素

    private static void findFirstFindAny() {
        Optional<Integer> first = Stream.of(null, 2, 3).findFirst(); // java.lang.NullPointerException
        Optional<Integer> one = Stream.of(1, 2, 3).findFirst(); // 1
        System.out.println(Stream.of(1, 2, 3).findAny().get()); // 其中一个,也会报空指针异常的可能
    }

21、通过构造器生成流

    private static void builder() {
        // add(T t)	添加元素到流,返回 Builder 自身(支持链式调用)
        // accept(T t)	与 add 功能相同,但返回 void(实现 Consumer 接口)
        // build()	构建最终的 Stream,之后不能再修改
        Stream.Builder<String> builder = Stream.builder();
        builder.add("Apple");
        builder.add("Banana").add("Cherry"); // 链式调用
        Stream<String> stream = builder.build();
        stream.forEach(System.out::println); // 输出:Apple Banana Cherry
    }

22、无限流的构造以及无限流的终止

    private static void iterate() {
        // static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
        // iterate是一个用于生成无限流或有限流的静态方法, 生成一个无限流,从初始值seed开始,依次应用UnaryOperator生成后续元素
        // 在java8中结合limit来限制个数,java9中允许通过条件(predicate)来终止迭代
        // 生成偶数序列:0, 2, 4, 6, ...
        Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);
        // 通常需要结合 limit() 截断无限流(java8)
        evenNumbers.limit(5).forEach(System.out::println); // 输出 0 2 4 6 8

        // 生成数字直到小于 10:0, 1, 2, ..., 9 (java9)
        Stream<Integer> numbers = Stream.iterate(
                0,
                n -> n < 10, // 条件:当 n < 10 时继续
                n -> n + 1   // 迭代函数
        );
        numbers.forEach(System.out::println); // 输出 0 到 9
    }