跳转至

Java Stream 教程:深入探索与实践

简介

Java Stream 是 Java 8 引入的一个强大功能,它为处理集合数据提供了一种简洁、高效且声明式的方式。Stream 允许你以函数式编程风格对数据集进行操作,使代码更加紧凑、易读,同时提升性能。本教程将深入介绍 Java Stream 的基础概念、使用方法、常见实践以及最佳实践,帮助你熟练掌握这一强大工具。

目录

  1. 基础概念
  2. 使用方法
    • 创建 Stream
    • 中间操作
    • 终止操作
  3. 常见实践
    • 过滤数据
    • 映射数据
    • 归约操作
    • 排序操作
  4. 最佳实践
    • 性能优化
    • 代码可读性
    • 并行 Stream
  5. 小结
  6. 参考资料

基础概念

Stream 是一系列支持顺序和并行聚合操作的元素。它不是数据结构,不存储数据,而是从数据源(如集合、数组)获取数据,并在其上进行各种操作。Stream 操作分为中间操作和终止操作: - 中间操作:返回一个新的 Stream,可链式调用多个中间操作。例如 filtermap 等。 - 终止操作:执行 Stream 操作链,并返回结果。例如 forEachcollect 等。

使用方法

创建 Stream

  1. 从集合创建
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamCreation {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "banana", "cherry");
        Stream<String> streamFromList = list.stream();
        Stream<String> parallelStreamFromList = list.parallelStream();
    }
}
  1. 从数组创建
import java.util.stream.Stream;

public class ArrayStream {
    public static void main(String[] args) {
        String[] array = {"one", "two", "three"};
        Stream<String> streamFromArray = Arrays.stream(array);
    }
}
  1. 使用 Stream.of 创建
import java.util.stream.Stream;

public class StreamOf {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("a", "b", "c");
    }
}

中间操作

  1. 过滤数据:使用 filter 方法根据条件过滤元素。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> evenNumbers = numbers.stream()
              .filter(n -> n % 2 == 0)
              .collect(Collectors.toList());
        System.out.println(evenNumbers); // 输出: [2, 4, 6]
    }
}
  1. 映射数据:使用 map 方法将元素转换为另一种形式。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class MapExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("hello", "world");
        List<Integer> wordLengths = words.stream()
              .map(String::length)
              .collect(Collectors.toList());
        System.out.println(wordLengths); // 输出: [5, 5]
    }
}
  1. 去重:使用 distinct 方法去除重复元素。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class DistinctExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
        List<Integer> distinctNumbers = numbers.stream()
              .distinct()
              .collect(Collectors.toList());
        System.out.println(distinctNumbers); // 输出: [1, 2, 3, 4, 5]
    }
}

终止操作

  1. 遍历元素:使用 forEach 方法对每个元素执行操作。
import java.util.Arrays;
import java.util.List;

public class ForEachExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry");
        fruits.stream()
              .forEach(System.out::println);
    }
}
  1. 收集结果:使用 collect 方法将 Stream 结果收集到集合中。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CollectExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> squaredNumbers = numbers.stream()
              .map(n -> n * n)
              .collect(Collectors.toList());
        System.out.println(squaredNumbers); // 输出: [1, 4, 9, 16, 25]
    }
}
  1. 归约操作:使用 reduce 方法将 Stream 中的元素归约为一个值。
import java.util.Arrays;
import java.util.List;

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int sum = numbers.stream()
              .reduce(0, Integer::sum);
        System.out.println(sum); // 输出: 15
    }
}

常见实践

过滤数据

在处理大量数据时,常常需要根据特定条件过滤出符合要求的数据。例如,从用户列表中筛选出年龄大于 18 岁的用户。

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

class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

public class UserFilterExample {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        users.add(new User("Alice", 16));
        users.add(new User("Bob", 20));
        users.add(new User("Charlie", 22));

        List<User> adultUsers = users.stream()
              .filter(user -> user.getAge() > 18)
              .collect(Collectors.toList());
        System.out.println(adultUsers.size()); // 输出: 2
    }
}

映射数据

将一种类型的数据转换为另一种类型是常见的需求。比如,将字符串列表中的每个字符串转换为大写形式。

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

public class StringMapExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("hello", "world");
        List<String> upperCaseWords = words.stream()
              .map(String::toUpperCase)
              .collect(Collectors.toList());
        System.out.println(upperCaseWords); // 输出: [HELLO, WORLD]
    }
}

归约操作

计算集合中元素的总和、乘积等操作可以使用归约操作。例如,计算整数列表的乘积。

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

public class ProductReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
        int product = numbers.stream()
              .reduce(1, (a, b) -> a * b);
        System.out.println(product); // 输出: 24
    }
}

排序操作

对集合中的元素进行排序也是常见的操作。可以使用 sorted 方法对 Stream 中的元素进行排序。

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

public class SortExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
        List<Integer> sortedNumbers = numbers.stream()
              .sorted()
              .collect(Collectors.toList());
        System.out.println(sortedNumbers); // 输出: [1, 1, 3, 4, 5, 9]
    }
}

最佳实践

性能优化

  • 使用并行 Stream:对于大规模数据集,并行 Stream 可以充分利用多核处理器的优势,提高处理速度。但要注意并行操作可能带来的线程安全问题。
  • 避免不必要的中间操作:尽量减少 Stream 操作链中的中间操作,以减少计算开销。

代码可读性

  • 保持操作链简洁:避免过长的操作链,可将复杂操作拆分成多个方法,提高代码的可读性和维护性。
  • 使用描述性的方法名:在自定义方法时,使用清晰、描述性的方法名,使代码意图一目了然。

并行 Stream

在使用并行 Stream 时,确保数据源是线程安全的。如果数据源不是线程安全的,可能会导致数据不一致或其他错误。例如,在多线程环境下对共享集合进行写操作时,应使用线程安全的集合类。

小结

Java Stream 为处理集合数据提供了一种强大而灵活的方式。通过理解基础概念、掌握使用方法以及遵循最佳实践,你可以编写出高效、易读的代码。Stream 的中间操作和终止操作使得数据处理变得简洁明了,无论是过滤、映射、归约还是排序等操作,都能轻松实现。希望本教程能帮助你在日常开发中熟练运用 Java Stream,提升开发效率。

参考资料