跳转至

深入理解 Java Stream

简介

在 Java 编程中,Stream 是一个强大的功能,它提供了一种更简洁、高效的方式来处理集合数据。Stream 允许你以声明式的方式处理数据序列,而不必编写复杂的循环和条件语句。本文将详细介绍 Java Stream 的基础概念、使用方法、常见实践以及最佳实践,帮助你更好地掌握和运用这一特性。

目录

  1. 基础概念
  2. 使用方法
    • 创建 Stream
    • 中间操作
    • 终端操作
  3. 常见实践
    • 过滤数据
    • 映射数据
    • 聚合操作
    • 排序
  4. 最佳实践
    • 避免不必要的装箱和拆箱
    • 合理使用并行 Stream
    • 注意 Stream 的短路操作
  5. 小结
  6. 参考资料

基础概念

Java Stream 是 Java 8 引入的新特性,它代表了一系列支持顺序和并行聚合操作的元素。Stream 并不是数据结构,它不会存储数据,而是提供了一种对数据源进行操作的方式。数据源可以是集合、数组、文件等。Stream 的操作可以分为中间操作和终端操作,中间操作返回一个新的 Stream,终端操作执行计算并返回结果。

使用方法

创建 Stream

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

    public class StreamExample { public static void main(String[] args) { List list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("cherry");

        // 创建顺序 Stream
        Stream<String> stream = list.stream();
    
        // 创建并行 Stream
        Stream<String> parallelStream = list.parallelStream();
    }
    

    } 2. **从数组创建**java import java.util.stream.Stream;

    public class ArrayStreamExample { public static void main(String[] args) { String[] array = {"apple", "banana", "cherry"}; Stream stream = Stream.of(array); } } ```

中间操作

  1. 过滤(filter):用于筛选出符合条件的元素。 ```java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;

    public class FilterExample { public static void main(String[] args) { List numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5);

        Stream<Integer> filteredStream = numbers.stream()
              .filter(n -> n % 2 == 0);
    }
    

    } 2. **映射(map)**:将一个元素转换为另一个元素。java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;

    public class MapExample { public static void main(String[] args) { List words = new ArrayList<>(); words.add("apple"); words.add("banana"); words.add("cherry");

        Stream<Integer> lengthStream = words.stream()
              .map(String::length);
    }
    

    } 3. **去重(distinct)**:去除 Stream 中的重复元素。java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;

    public class DistinctExample { public static void main(String[] args) { List numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(2); numbers.add(3); numbers.add(3);

        Stream<Integer> distinctStream = numbers.stream()
              .distinct();
    }
    

    } ```

终端操作

  1. 遍历(forEach):对 Stream 中的每个元素执行指定的操作。 ```java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;

    public class ForEachExample { public static void main(String[] args) { List list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("cherry");

        list.stream()
              .forEach(System.out::println);
    }
    

    } 2. **收集(collect)**:将 Stream 中的元素收集到一个集合中。java import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream;

    public class CollectExample { public static void main(String[] args) { List numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5);

        List<Integer> evenNumbers = numbers.stream()
              .filter(n -> n % 2 == 0)
              .collect(Collectors.toList());
    }
    

    } 3. **归约(reduce)**:将 Stream 中的元素组合成一个值。java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;

    public class ReduceExample { public static void main(String[] args) { List numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5);

        int sum = numbers.stream()
              .reduce(0, Integer::sum);
    }
    

    } ```

常见实践

过滤数据

在处理集合数据时,经常需要根据某些条件过滤出符合要求的元素。例如,从一个员工列表中筛选出年龄大于 30 岁的员工:

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

class Employee {
    private String name;
    private int age;

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

    public int getAge() {
        return age;
    }
}

public class EmployeeFilterExample {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("Alice", 25));
        employees.add(new Employee("Bob", 35));
        employees.add(new Employee("Charlie", 40));

        Stream<Employee> filteredEmployees = employees.stream()
              .filter(e -> e.getAge() > 30);
    }
}

映射数据

映射操作可以将一种类型的元素转换为另一种类型。例如,将一个字符串列表中的每个字符串转换为大写形式:

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

public class StringMapExample {
    public static void main(String[] args) {
        List<String> words = new ArrayList<>();
        words.add("apple");
        words.add("banana");
        words.add("cherry");

        Stream<String> upperCaseWords = words.stream()
              .map(String::toUpperCase);
    }
}

聚合操作

聚合操作可以对 Stream 中的元素进行计算,如求和、求平均值、最大值、最小值等。例如,计算一个整数列表的总和:

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

public class AggregationExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(4);
        numbers.add(5);

        int sum = numbers.stream()
              .mapToInt(Integer::intValue)
              .sum();
    }
}

排序

可以对 Stream 中的元素进行排序。例如,对一个整数列表进行升序排序:

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

public class SortingExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(3);
        numbers.add(1);
        numbers.add(4);
        numbers.add(2);

        Stream<Integer> sortedStream = numbers.stream()
              .sorted();
    }
}

最佳实践

避免不必要的装箱和拆箱

在使用 Stream 时,尽量使用原始类型的 Stream,如 IntStream、LongStream、DoubleStream 等,以避免不必要的装箱和拆箱操作,提高性能。

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

public class PrimitiveStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(4);
        numbers.add(5);

        // 使用 IntStream 避免装箱和拆箱
        IntStream intStream = numbers.stream()
              .mapToInt(Integer::intValue);
    }
}

合理使用并行 Stream

并行 Stream 可以利用多核处理器提高处理速度,但并非所有场景都适合使用并行 Stream。在使用并行 Stream 时,要考虑数据量、计算复杂度以及是否存在共享状态等因素。例如,对于小数据量的操作,并行 Stream 可能反而会带来额外的开销。

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

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

        // 合理使用并行 Stream
        Stream<Integer> parallelStream = numbers.parallelStream()
              .filter(n -> n % 2 == 0);
    }
}

注意 Stream 的短路操作

一些终端操作(如 findFirst、anyMatch 等)是短路操作,即一旦满足条件就会停止处理。在编写代码时,可以利用这些短路操作提高效率。

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

public class ShortCircuitExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(4);
        numbers.add(5);

        boolean anyEven = numbers.stream()
              .anyMatch(n -> n % 2 == 0);
    }
}

小结

Java Stream 为处理集合数据提供了一种强大而简洁的方式。通过理解基础概念、掌握使用方法、熟悉常见实践以及遵循最佳实践,你可以在编写代码时更加高效地利用 Stream,提高代码的可读性和性能。希望本文能够帮助你深入理解并熟练运用 Java Stream。

参考资料