引言

随着Java 8的发布,Stream API成为了Java开发人员手中的强大工具,它极大地简化了集合数据的操作,使得代码更简洁、更易于理解。本文将深入探讨Stream API的基本概念、优势以及如何使用它来处理数据。

Stream API简介

Stream API是Java 8引入的一项重要功能,它提供了一种新的处理数据的方式——流式处理(declarative programming)。与传统的循环迭代不同,Stream API允许开发者以声明式的方式来处理数据,这意味着开发者只需要告诉程序“要做什么”,而不是“怎么做”。

Stream API的关键特性

  1. 懒惰求值:Stream操作分为中间操作和终端操作,中间操作不会立即执行,只有当终端操作被触发时才会执行。
  2. 管道式编程:多个Stream操作可以串联起来形成流水线,这样可以减少代码量并提高可读性。
  3. 并行处理:Stream API天然支持并行处理,可以通过简单的设置实现对数据的并行处理,提高处理效率。

Stream API基本概念

数据源

  • 集合框架 (Collection, Set, List)
  • 数组
  • I/O通道

Stream

  • 数据载体:Stream并不存储数据,而是承载数据。
  • 操作数据:通过一系列方法调用来操作数据。

操作类型

  • 中间操作:返回一个新的Stream,例如filter(), map(), sorted()等。
  • 终端操作:执行计算并返回结果,例如forEach(), collect(), reduce(), count()等。

使用示例

创建Stream

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        // 从集合创建Stream
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Stream<Integer> stream = numbers.stream();

        // 从数组创建Stream
        Integer[] array = {1, 2, 3, 4, 5};
        Stream<Integer> arrayStream = Arrays.stream(array);

        // 从值创建Stream
        Stream<Integer> valueStream = Stream.of(1, 2, 3, 4, 5);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

中间操作

public class StreamIntermediateOperations {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 过滤偶数
        Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0);

        // 映射转换
        Stream<String> stringStream = numbers.stream().map(n -> n + " is a number");

        // 排序
        Stream<Integer> sortedStream = numbers.stream().sorted();

        // 限制数量
        Stream<Integer> limitedStream = numbers.stream().limit(3);

        // 跳过前几个元素
        Stream<Integer> skipStream = numbers.stream().skip(2);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

终端操作

public class StreamTerminalOperations {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 遍历输出
        numbers.stream().forEach(System.out::println);

        // 收集到列表
        List<Integer> squaredList = numbers.stream()
                                           .map(n -> n * n)
                                           .collect(Collectors.toList());

        // 计算总和
        int sum = numbers.stream().reduce(0, Integer::sum);

        // 计算元素个数
        long count = numbers.stream().count();

        // 找到最大值
        Optional<Integer> max = numbers.stream().max(Integer::compare);

        // 找到最小值
        Optional<Integer> min = numbers.stream().min(Integer::compare);

        // 分组
        Map<Boolean, List<Integer>> evenOddMap = numbers.stream()
                                                       .collect(Collectors.groupingBy(n -> n % 2 == 0));

        // 归约
        Optional<Integer> product = numbers.stream().reduce((a, b) -> a * b);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

并行流

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 使用并行流
        long parallelCount = numbers.parallelStream().count();
        System.out.println("Parallel count: " + parallelCount);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

性能考虑

虽然Stream API提供了很多便利,但在某些情况下,传统的循环可能更为高效。例如,在处理大数据集时,应当考虑是否需要并行流;对于小数据集,直接使用循环可能会更快。

用对象演示Stream流操作

前面的例子中我们都是使用Integer整数演示的,但是实际项目中我们更多的是使用对象去操作,下面我们通过对象演示一下。

首先,我们定义一个Person类:

public class Person {
    private String name;
    private int age;
    private boolean isStudent;

    public Person(String name, int age, boolean isStudent) {
        this.name = name;
        this.age = age;
        this.isStudent = isStudent;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public boolean isStudent() {
        return isStudent;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", isStudent=" + isStudent +
                '}';
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

接下来,我们将创建一个包含Person对象的列表,并使用Stream API来处理这些对象。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class PersonStreamExample {

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>(Arrays.asList(
                new Person("Alice", 30, false),
                new Person("Bob", 25, true),
                new Person("Charlie", 22, true),
                new Person("David", 27, false),
                new Person("Eve", 20, true)
        ));

        // 1. 过滤学生
        List<Person> students = filterStudents(people);
        printList(students);

        // 2. 映射姓名
        List<String> names = mapNames(people);
        printList(names);

        // 3. 排序按年龄
        List<Person> sortedByAge = sortByAge(people);
        printList(sortedByAge);

        // 4. 限制前两个
        List<Person> topTwo = limitTopTwo(people);
        printList(topTwo);

        // 5. 分组学生和非学生
        Map<Boolean, List<Person>> groupedByStudentStatus = groupByStudentStatus(people);
        printGroup(groupedByStudentStatus);
    }

    private static List<Person> filterStudents(List<Person> people) {
        return people.stream()
                     .filter(person -> person.isStudent())
                     .collect(Collectors.toList());
    }

    private static List<String> mapNames(List<Person> people) {
        return people.stream()
                     .map(Person::getName)
                     .collect(Collectors.toList());
    }

    private static List<Person> sortByAge(List<Person> people) {
        return people.stream()
                     .sorted((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()))
                     .collect(Collectors.toList());
    }

    private static List<Person> limitTopTwo(List<Person> people) {
        return people.stream()
                     .limit(2)
                     .collect(Collectors.toList());
    }

    private static Map<Boolean, List<Person>> groupByStudentStatus(List<Person> people) {
        return people.stream()
                     .collect(Collectors.groupingBy(Person::isStudent));
    }

    private static void printList(List<?> list) {
        list.forEach(System.out::println);
        System.out.println();
    }

    private static void printGroup(Map<Boolean, List<Person>> group) {
        group.forEach((key, value) -> {
            System.out.println(key + ":");
            value.forEach(System.out::println);
            System.out.println();
        });
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.

这段代码演示了以下操作:

  1. 过滤学生:使用filter()方法找到所有学生。
  2. 映射姓名:使用map()方法提取每个人的姓名。
  3. 排序按年龄:使用sorted()方法按照年龄对人员进行排序。
  4. 限制前两个:使用limit()方法只保留前两个元素。
  5. 分组学生和非学生:使用groupingBy()方法根据是否是学生的状态对学生进行分组。

结论

Stream API是Java 8带来的重大革新之一,它不仅让代码更简洁、更易于维护,而且提高了代码的可读性和扩展性。掌握Stream API对于现代Java开发人员来说至关重要。