💖2025年不会Stream流被同事排挤了┭┮﹏┭┮(强烈建议实操)

一、Stream流的特点和使用过程

Stream API允许开发者以声明性方式处理数据集合。可以简化复杂的数据操作,并且支持并行处理以提高性能。

1.1 特点

  • 声明性: Stream API允许你描述你想要做什么,而不是详细说明怎么做。
  • 链式操作: 可将多个操作链接在一起,形成一个流水线,每个操作都会生成一个新的流供下一个操作使用。
  • 函数式编程: Stream API鼓励使用无副作用的函数和 lambda 表达式。
  • 并行处理: Stream API支持并行流,可以充分利用多核处理器。
  • 延迟执行: Stream 操作是惰性的,只有在终端操作(如 collect、forEach)被调用时,整个流水线才会执行。
  • 短路操作: 某些终端操作(如 anyMatch、allMatch、noneMatch、findFirst)在找到结果后会立即停止处理。

1.2 使用过程

我们先来了解使用Stream流的四个过程,然后再逐渐深入了解每个过程分别都有哪些操作。

1.2.1 创建流

从数据源(如集合、数组、文件等)创建一个流。

java 复制代码
// 创建Stream
ArrayList<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 6);
Stream<Integer> stream = list.stream();

1.2.2 中间操作

对流进行一系列转换操作,如 filter(过滤)、map(映射)、sorted(排序)等。这些操作会返回一个新的流,不会立即执行。

java 复制代码
// 中间操作,比如映射、过滤、排序等,这里我们以映射为例,将每个元素+1
Stream<Integer> integerStream = stream.map(i -> i + 1);

1.2.3 终端操作

执行一个终端操作来结束流的处理并产生结果。终端操作会触发整个流水线的执行,并且不会返回一个新的流。

java 复制代码
// 终端操作,比如收集、聚合、forEach等,这里我们以收集为例
List<Integer> collect = integerStream.collect(Collectors.toList());

1.2.4 处理结果

使用终端操作返回的结果进行后续处理。

java 复制代码
// 处理结果
collect.forEach(System.out::println);

流只能被使用一次。一旦终端操作被触发,流就会被关闭,无法再次使用。

二、Stream流的魅力

2.1 需求背景

假如现在有一群整型数据,需求是:

  1. 提取出奇数和偶数
  2. 找到奇数和偶数各自的最大值和最小值

2.1 没有使用Stream前

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 原始数据
        List<Integer> list =Arrays.asList(1, 2, 3, 4, 5, 6);
        Map<Boolean, List<Integer>> map = new HashMap<>(2);

        // 创建数数集合和偶数集合
        List<Integer> ji = new ArrayList<>();
        List<Integer> ou = new ArrayList<>();
        // 遍历集合,将偶数放入偶数集合,将奇数放入奇数集合
        for (Integer i : list) {
            if (i % 2 == 0) {
                ou.add(i);
            } else {
                ji.add(i);
            }
        }
        // 各自排序
        ji.sort(Integer::compareTo);
        ou.sort(Integer::compareTo);

        // 奇数数组找到最大值和最小值
        if(ji.size() > 1) {
            map.put(Boolean.FALSE, Arrays.asList(ji.get(0), ji.get(ji.size() - 1)));
        } else if (ji.size() == 1) {
            map.put(Boolean.FALSE, Arrays.asList(ji.get(0), ji.get(0)));
        } else {
            map.put(Boolean.FALSE, Collections.emptyList());
        }

        // 偶数数组找到最大值和最小值
        if(ou.size() > 1) {
            map.put(Boolean.TRUE, Arrays.asList(ou.get(0), ou.get(ou.size() - 1)));
        } else if (ou.size() == 1) {
            map.put(Boolean.TRUE, Arrays.asList(ou.get(0), ou.get(0)));
        } else {
            map.put(Boolean.TRUE, Collections.emptyList());
        }

        // 奇数数组,偶数数组,各自的最大值和最小值
        // 输出结果:奇数数组和偶数数组的最小值和最大值
        map.forEach((k, v) -> System.out.println((k ? "偶数" : "奇数") + "数组: 最小值 = " + v.get(0) + ", 最大值 = " + v.get(1)));
    }
}

输出结果:

java 复制代码
奇数数组: 最小值 = 1, 最大值 = 5
偶数数组: 最小值 = 2, 最大值 = 6

2.2 有使用Stream流后

我们可以发现当我们有使用Stream流后代码的简洁的会提高不少。

java 复制代码
public class Main {
    public static void main(String[] args) {
        Map<Boolean, List<Integer>> map = Stream.of(1, 2, 3, 4, 5, 6)
                .collect(Collectors.groupingBy(
                        x -> x % 2 == 0,
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                list -> {
                                    Collections.sort(list);
                                    return Arrays.asList(list.get(0), list.get(list.size() - 1));
                                }
                        )
                ));

        // 输出结果:奇数数组和偶数数组的最小值和最大值
        map.forEach((k, v) -> System.out.println((k ? "偶数" : "奇数") + "数组: 最小值 = " + v.get(0) + ", 最大值 = " + v.get(1)));
    }
}

输出结果:

java 复制代码
奇数数组: 最小值 = 1, 最大值 = 5
偶数数组: 最小值 = 2, 最大值 = 6

三、Steam流创建

3.1 通过集合创建

调用集合对象的stream()方法来获取一个流

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 调用集合对象创建的stream获取一个流
        Stream<String> stream = Arrays.asList("a", "b", "c").stream();
        stream.forEach(System.out::println);
    }
}

输出结果:

java 复制代码
a
b
c

3.2 通过数组创建

使用Arrays.stream()方法从数组创建流,这适用于任何类型的数组。基本类型的数组,Arrays.stream()会返回特定类型的流,如IntStream、LongStream或DoubleStream。如果需要将这些流转换为通用Stream,你可以使用boxed()方法。

java 复制代码
public class Main {
    public static void main(String[] args) {
        int[] array = {1, 2, 3, 4, 5};
        IntStream intStream = Arrays.stream(array);
        intStream.min().ifPresent(System.out::println);

        String[] strArray = {"a", "b", "c"};
        Stream<String> stringStream = Arrays.stream(strArray);
        System.out.println(stringStream.collect(Collectors.toList()));
    }
}

输出结果:

java 复制代码
1
[a, b, c]

3.3 通过 Stream 静态方法创建

Stream类提供了几个静态方法来创建流。

java 复制代码
public class Main {
    public static void main(String[] args) {
        Stream<String> strStream = Stream.of("a", "b", "c");
        strStream.forEach(System.out::println);
    }
}

输出结果:

java 复制代码
a
b
c

3.4 通过随机数生成

Random类可以用于生成随机数,并且你可以使用ints()、longs()或doubles()方法创建一个流。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 生成10个0到100之间的随机数
        IntStream ints = new Random().ints(10, 0, 100);
        ints.forEach(System.out::println);
    }
}

输出结果:

java 复制代码
56
20
47
19
83
0
28
92
95
3

3.5 通过文件I/O

在处理文件时,可以使用Files类中的方法,如lines(),从文件中读取行并创建一个流。

java 复制代码
public class Main {
    public static void main(String[] args) {
        try {
            Path path = Paths.get("path/to/file.txt");
            Stream<String> linesStream = Files.lines(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

111
222
333

3.6 无限流创建

Stream API 提供了能够生成无限序列的流,但通常会结合limit()方法来限制流的长度。

  • Stream.iterate(T seed, UnaryOperatorf): 创建一个无限顺序的流,通过反复应用函数f来生成元素。
  • Stream.generate(Suppliers): 创建一个无限无序的流,其中每个元素由提供的Supplier生成。
java 复制代码
public class Main {
    public static void main(String[] args) {
        // 使用iterate生成一个无限递增的整数流
        Stream<Integer> intStream = Stream.iterate(0, i -> i + 1).limit(10);
        intStream.forEach(System.out::println);
    }
}

输出结果:

java 复制代码
0
1
2
3
4
5
6
7
8
9

3.7 通过范围创建

创建一个包含从startInclusive(包含)到endExclusive(不包含)之间的整数的流。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 使用generate生成一个无限的随机数流
        IntStream intStream = Stream.generate(Math::random).limit(10).mapToInt(x -> (int) (x * 100));
        intStream.forEach(System.out::println);
    }
}

输出结果:

java 复制代码
54
46
57
43
20
98
46
20
9
68

四、中间操作

4.1 filter(过滤)

filter方法用于过滤流中的元素。

java 复制代码
@Test
void processStream() {
    // 使用filter过滤出非空元素
    List<String> collect = Arrays.asList("a", "b", "c", null, "d", null)
    .stream()
    .filter(Objects::nonNull)
    .collect(Collectors.toList());
    collect.forEach(System.out::println);
}

输出结果:

java 复制代码
abcd

4.2 map(转换)

map方法用于对流中的每个元素执行某种操作,并返回一个新的流。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 使用filter过滤出非空元素,并且对每个字符加上"-"
        List<String> collect = Arrays.asList("a", "b", "c", null, "d", null)
                .stream()
                .filter(Objects::nonNull)
                .map(x -> "-" + x + "-")
                .collect(Collectors.toList());
        collect.forEach(System.out::println);
    }
}

输出结果:

java 复制代码
-a-
-b-
-c-
-d-

此外streamAPI中还有指定类型的map方法:

  • mapToInt(ToIntFunction<? super T> mapper): 将流中的元素转换成int类型。
  • mapToLong(ToLongFunction<? super T> mapper): 将流中的元素转换成long类型。
  • mapToDouble(ToDoubleFunction<? super T> mapper): 将流中的元素转换成double类型。

4.3 flatMap(转换)

flatMap方法在Java Stream API中用于将流中的每个元素转换成一个新的流,然后将这些新生成的流合并成一个单一的流。用于处理流中的集合或数组元素,以将它们"展平"成一个单一的元素流。

一个包含字符串列表的列表使用flatMap将其转换成一个包含所有字符串的单一流:

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("b", "c"), Arrays.asList("c", "d"));
        List<String> collect = list.stream()
                .flatMap(x -> x.stream())
                .collect(Collectors.toList());
        collect.forEach(System.out::print);
    }
}

输出结果:

java 复制代码
abbccd

4.4 distince(去重)

distinct方法用于去除流中的重复元素。基于元素的 equals 和 hashCode 方法来确定哪些元素是重。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("b", "c"), Arrays.asList("c", "d"));
        List<String> collect = list.stream()
                .flatMap(x -> x.stream())
                .distinct()
                .collect(Collectors.toList());
        collect.forEach(System.out::print);
    }
}

输出结果:

java 复制代码
abcd

4.5 Limit(限制)/Skip(跳过)/Peek(展示)

limit用于限制流中的元素数量,skip用于跳过流中的前N个元素,而peek则允许对流中的每个元素执行某种操作(如打印、修改等)而不改变流本身。peek通常用于调试或查看流中的元素。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("b", "c"), Arrays.asList("c", "d"));
        List<String> collect = list.stream()
                .flatMap(x -> x.stream())
                .distinct()
                .skip(2)
                .limit(2)
                .peek(System.out::print)
                .collect(Collectors.toList());
        System.out.println();
        System.out.println(collect);
    }
}

输出结果:

java 复制代码
cd
[c, d]

4.6 Sorted(排序)

排序可以通过sorted()方法实现,该方法有两种形式:

  • 无参的sorted(),它使用元素的自然顺序进行排序(要求元素实现Comparable接口);
  • 以及接受Comparator参数的sorted(Comparator<? super T> comparator),它允许自定义排序规则。
java 复制代码
public class Main {
    public static void main(String[] args) {
        // 1.使用无限流生成10个1 ~ 100 范围内的随机数 升序打印
        IntStream sorted1 = new Random().ints(10, 1, 100).sorted();
        sorted1.forEach(x -> System.out.print(x + "-"));
        System.out.println();

        // 2.自定义排序规则,降序打印
        List<Integer> numbers = Arrays.asList(5, 2, 9, 1, 7);
        List<Integer> collect = numbers.stream().sorted((x, y) -> y - x).collect(Collectors.toList());
        collect.forEach(x -> System.out.print(x + "-"));
    }
}

输出结果:

java 复制代码
24-32-32-33-41-49-62-63-68-88-
9-7-5-2-1-

4.7 concat(两个流连接成一个流)

concat(Stream<? extends T> a, Stream<? extends T> b): 静态方法,用于将两个流连接成一个流。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建两个整数列表
        List<Integer> list1 = Arrays.asList(1, 2, 3);
        List<Integer> list2 = Arrays.asList(4, 5, 6);

        // 将两个列表转换为流,并使用 concat 方法连接它们
        Stream<Integer> concatenatedStream = Stream.concat(list1.stream(), list2.stream());
        // 使用连接后的流进行一些操作,比如打印所有元素
        concatenatedStream.forEach(System.out::println);
    }
}

输出结果:

java 复制代码
123456

五、终端操作

5.1 forEach/findFirst/findAny(查询)

Stream API中,forEach、findFirst和findAny都是终端操作。forEach用于迭代流中的每个元素并执行一个操作,findFirst用于获取流中的第一个元素,而findAny则用于获取流中的任意元素(并行流特别有用,因为它可能更快)。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h");
        // 使用forEach循环
        list.stream().forEach(System.out::print);
        System.out.println();

        // 使用findFirst找到集合中第一个元素
        String orElse = list.stream().findFirst().orElse(null);
        System.out.print(orElse);
        System.out.println();

        // 使用findAny找到任意一个元素
        String anElse = list.stream().findAny().orElse(null);
        System.out.print(anElse);
        System.out.println();
    }
}

输出结果如下:

java 复制代码
abcdefgh
a
a

5.2 count/sum/max/min(汇总)

count、sum、max和min都是终端操作,用于对流中的元素进行计数、求和、找最大值和最小值。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);

        // 使用count统计集合元素个数
        long count = list.stream().count();
        System.out.println("集合元素个数为:" + count);

        // 使用sum统计集合元素总和
        int sum = list.stream().mapToInt(Integer::intValue).sum();
        System.out.println("集合元素总和:" + sum);

        // 使用max找到集合元素最大值
        int max = list.stream().mapToInt(Integer::intValue).max().orElse(0);
        System.out.println("集合元素最大值:" + max);

        // 使用min找到集合元素最小值
        int min = list.stream().mapToInt(Integer::intValue).min().orElse(0);
        System.out.println("集合元素最小值:" + min);
    }
}

输出结果如下:

java 复制代码
集合元素个数为:16
集合元素总和:136
集合元素最大值:16
集合元素最小值:1

5.3 anyMatch/allMatch/noneMatch(匹配)

anyMatch、allMatch和noneMatch是终端操作,用于检查流中的元素是否满足给定的谓词(条件)。anyMatch检查是否有任何元素满足条件,allMatch检查是否所有元素都满足条件,而noneMatch检查是否没有任何元素满足条件。

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);

        // 使用 anyMatch 判断 大于5的元素 是否存在
        boolean anyMatch = list.stream().mapToInt(Integer::valueOf).anyMatch(x -> x > 5);
        System.out.println("是否存在大于5的元素:" + anyMatch);

        // 使用 allMatch 判断 集合中的元素是否全部大于 5
        boolean allMatch = list.stream().mapToInt(Integer::valueOf).allMatch(x -> x > 5);
        System.out.println("是否全部元素大于5:" + allMatch);

        // 使用 noneMatch 判断 集中中的元素是否都不满足 不为 null
        List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, null, 11, 12, 13, 14, 15, 16);
        boolean noneMatch = list1.stream().noneMatch(x -> x != null);
        System.out.println("集合中元素是否都不为null:" + noneMatch);
    }
}

输出结果如下:

java 复制代码
是否存在大于5的元素:true
是否全部元素大于5:false
集合中元素是否都不为null:false

5.4 reduce(归约)

reduce方法是一个终端操作,用于将流中的所有元素组合成一个单一的结果。它通常用于执行某种累积操作,比如计算元素的总和、乘积或连接字符串等。

reduce(T identity, BinaryOperatoraccumulator) 此方法接受一个初始值和一个累积函数,用于归约流中的元素。

reduce(BinaryOperatoraccumulator) 此方法不接受初始值,而是使用流中的第一个元素作为初始值,然后应用累积函数。

计算一个员工列表中所有员工的总薪水,同时找出薪水最高的员工。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个员工列表
        List<Employee> employees = Arrays.asList(
                new Employee("Alice", 5000.0),
                new Employee("Bob", 6000.0),
                new Employee("Charlie", 5500.0),
                new Employee("David", 6500.0)
        );
        // 计算总薪资
        Double reduce = employees.stream().reduce(0.0, (acc, employee) -> acc + employee.getSalary(), Double::sum);
        System.out.println("各个员工总薪资为:" + reduce);

        // 找到薪资最高的员工
        Employee employee = employees.stream().max(Comparator.comparingDouble(Employee::getSalary)).orElse(null);
        System.out.println("薪资最高的员工薪资为:" + employee.getSalary());

    }
}

@AllArgsConstructor
@Data
class Employee {
    private String name;
    private double salary;
}

输出结果如下:

java 复制代码
各个员工总薪资为:23000.0
薪资最高的员工薪资为:6500.0

5.5 collect(收集)

collect方法在Java Stream API中通常用于收集流中的元素到某种集合或其他数据结构中。

collect(Suppliersupplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner):

重载版本的collect方法提供了更高的灵活性,允许你自定义收集过程。 这个collect方法接受三个参数:

Suppliersupplier:一个供应器,用于创建新的结果容器。

BiConsumer<R, ? super T> accumulator:一个累加器,用于将流中的元素添加到结果容器中。

BiConsumer<R, R> combiner:一个组合器,用于合并两个结果容器(通常用于并行流)。

自定义一个收集过程,将流中的字符串连接成一个单独的字符串:

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个员工列表
        List<Employee> employees = Arrays.asList(
                new Employee("Alice", 5000.0),
                new Employee("Bob", 6000.0),
                new Employee("Charlie", 5500.0),
                new Employee("David", 6500.0)
        );

        String string = employees.stream()
                .map(Employee::getName)
                .collect(
                        StringBuilder::new,
                        (sb, name) -> {
                            if (!sb.isEmpty()) {
                                sb.append(" - ");
                            }
                            sb.append(name);
                        },
                        StringBuilder::append
                ).toString();
        System.out.println(string);
    }
}

@AllArgsConstructor
@Data
class Employee {
    private String name;
    private double salary;
}

输出结果如下:

java 复制代码
Alice - Bob - Charlie - David

5.6 toList/toMap/toSet/toArray(转集合)

toList(), toMap(), 和 toSet() 是非常有用的终端操作,它们可以将流中的元素收集到相应的集合中

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个员工列表
        List<Employee> employees = Arrays.asList(
                new Employee("Alice", 5000.0),
                new Employee("Bob", 6000.0),
                new Employee("Charlie", 5500.0),
                new Employee("David", 6500.0)
        );

        // toList
        List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
        System.out.println(list);

        // toSet
        Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
        System.out.println(set);

        // toMap
        Map<String, Employee> collect = employees.stream().collect(Collectors.toMap(Employee::getName, Function.identity()));
        System.out.println(collect);

    }
}
@AllArgsConstructor
@Data
class Employee {
    private String name;
    private double salary;
}

输出结果如下:

java 复制代码
[Alice, Bob, Charlie, David]
[Bob, Alice, Charlie, David]
{Bob=Employee(name=Bob, salary=6000.0), Alice=Employee(name=Alice, salary=5000.0), Charlie=Employee(name=Charlie, salary=5500.0), David=Employee(name=David, salary=6500.0)}

5.7 summing/averaging/summarizing(统计)

Collectors 类提供了几个用于数据统计的收集器,如 averagingDouble、summarizingDouble 和 summingDouble。这些收集器通常与流的 collect 方法一起使用,用于对数值流(如员工薪水)进行统计。 Collectors提供了一系列用于数据统计的静态方法: 计数:count 平均值:averagingInt、averagingLong、averagingDouble 最值:maxBy、minBy 求和:summingInt、summingLong、summingDouble 统计以上所有:summarizingInt、summarizingLong、summarizingDouble

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个员工列表
        List<Employee> employees = Arrays.asList(
                new Employee("Alice", 5000.0),
                new Employee("Bob", 6000.0),
                new Employee("Charlie", 5500.0),
                new Employee("David", 6500.0)
        );

        // 统计平均薪水
        Double averaging = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(averaging);

        // 统计薪资总和
        Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        // 获取薪资的统计信息(包含:count, sum, min, average, max)
        DoubleSummaryStatistics collect = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(collect);
    }
}
@AllArgsConstructor
@Data
class Employee {
    private String name;
    private double salary;
}

输出结果如下:

java 复制代码
5750.0
23000.0
DoubleSummaryStatistics{count=4, sum=23000.000000, min=5000.000000, average=5750.000000, max=6500.000000}

5.8 summaryStatistics(统计)

对于数值流(如 IntStream, LongStream, DoubleStream),summaryStatistics返回描述该流统计信息的对象,如最小值、最大值、平均值等。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个包含一些双精度浮点数的数组
        double[] numbers = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};

        // 使用 DoubleStream 的 of 方法创建一个流,然后调用 summaryStatistics 方法
        DoubleSummaryStatistics stats = Arrays.stream(numbers).summaryStatistics();

        // 输出统计信息
        System.out.println("最小值: " + stats.getMin());
        System.out.println("最大值: " + stats.getMax());
        System.out.println("平均值: " + stats.getAverage());
        System.out.println("元素数量: " + stats.getCount());
        System.out.println("元素总和: " + stats.getSum());

    }
}

输出结果如下:

java 复制代码
最小值: 1.0
最大值: 10.0
平均值: 5.5
元素数量: 10
元素总和: 55.0

5.9 joining(拼接)

Collectors.joining可以将流中的元素连接成一个字符串。这对将列表、集合或其他流数据结构转换为单个字符串表示形式特别有用。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个员工列表
        List<Employee> employees = Arrays.asList(
                new Employee("Alice", 5000.0),
                new Employee("Bob", 6000.0),
                new Employee("Charlie", 5500.0),
                new Employee("David", 6500.0)
        );

        // 将各个员工的名称用 - 符号连接起来
        String collect = employees.stream().map(Employee::getName).collect(Collectors.joining("-"));
        System.out.println(collect);

    }
}
@AllArgsConstructor
@Data
class Employee {
    private String name;
    private double salary;
}

输出结果如下:

java 复制代码
Alice-Bob-Charlie-David

5.10 分组(partitioningBy/groupingBy)

Collectors.partitioningBy 方法用于根据提供的谓词(Predicate)对流中的元素进行分区。

Collectors.groupingBy 方法用于根据提供的分类函数对流中的元素进行分组。在这个例子中,分类函数是 Employee::getDepartment,它根据员工的部门对员工进行分组。结果是一个映射,其中键是部门名称,值是对应部门的员工列表。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 创建一个员工列表
        List<Employee> employees = Arrays.asList(
                new Employee("Alice", 5000.0, "软件按部"),
                new Employee("Bob", 6000.0, "市场部"),
                new Employee("Charlie", 5500.0, "后勤部"),
                new Employee("David", 6500.0,"软件按部")
        );

        // 使用partitioning 自定义分区 这里我们以薪资大于5000为分区
        Predicate<Employee> salaryAbove5000 = employee -> employee.getSalary() > 5000;
        Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy(salaryAbove5000));
        System.out.println(collect);

        // 按员工属性【部门】进行分区
        Map<String, List<Employee>> collect1 = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));
        System.out.println(collect1);
    }
}

@AllArgsConstructor
@Data
class Employee {
    private String name;
    private double salary;
    private String department;
}

输出结果如下:

java 复制代码
{false=[Employee(name=Alice, salary=5000.0, department=软件按部)], true=[Employee(name=Bob, salary=6000.0, department=市场部), Employee(name=Charlie, salary=5500.0, department=后勤部), Employee(name=David, salary=6500.0, department=软件按部)]}
{后勤部=[Employee(name=Charlie, salary=5500.0, department=后勤部)], 市场部=[Employee(name=Bob, salary=6000.0, department=市场部)], 软件按部=[Employee(name=Alice, salary=5000.0, department=软件按部), Employee(name=David, salary=6500.0, department=软件按部)]}

六、顺序流和并行流

parallel和sequential是用来指定流的执行模式的方法。这两种模式决定了流中的元素是如何被处理的。

6.1 sequential(顺序流)

对一个流调用sequential()时,这个流以顺序方式执行操作。顺序流中的元素按照它们在数据源中出现的顺序逐个进行处理。顺序流是在单个线程中执行的,因此不存在线程安全问题。

java 复制代码
public class StreamPerformanceComparison {
    public static void main(String[] args) {
        int n = 100; // 数据规模

        // 测试顺序流
        long sequentialStart = System.currentTimeMillis();
        // 模拟耗时操作
        IntStream.range(1, n + 1)
        .sequential()
        .forEach(StreamPerformanceComparison::process);
        long sequentialEnd = System.currentTimeMillis();
        System.out.println("Sequential time: " + (sequentialEnd - sequentialStart) + " ms");
    }

    // 模拟耗时操作
    private static void process(int i) {
        try {
            Thread.sleep(10); // 模拟每个元素需要 10 毫秒处理时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果如下:

java 复制代码
Sequential time: 1544 ms

5.2 parallel(并行流)

调用parallelStream()或者对一个已经存在的流调用parallel()时,这个流以并行方式执行操作。并行流会尝试利用多个线程来同时处理多个元素,以提高处理速度。并行流是基于Java的ForkJoinPool实现的,它是一个特殊的线程池,适合执行可以并行处理的任务。

java 复制代码
public class StreamPerformanceComparison {
    public static void main(String[] args) {
        int n = 100; // 数据规模

        // 测试并行流
        long parallelStart = System.currentTimeMillis();
        // 模拟耗时操作
        IntStream.range(1, n + 1)
        .parallel()
        .forEach(StreamPerformanceComparison::process);
        long parallelEnd = System.currentTimeMillis();
        System.out.println("Parallel time: " + (parallelEnd - parallelStart) + " ms");
    }

    // 模拟耗时操作
    private static void process(int i) {
        try {
            Thread.sleep(10); // 模拟每个元素需要 10 毫秒处理时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果如下:

java 复制代码
Parallel time: 61 ms
相关推荐
盛派网络小助手1 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
∝请叫*我简单先生1 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
zquwei2 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
dessler3 小时前
Docker-run命令详细讲解
linux·运维·后端·docker
Q_19284999063 小时前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
Yuan_o_4 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺5 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白5 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构