Java Stream中的API你都用过了吗?

公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。

在本教程中,您将通过大量示例来学习 Java 8 Stream API。

Java 在 Java 8 中提供了一个新的附加包,称为 java.util.stream。该包由类、接口和枚举组成,允许对元素进行函数式操作。 您可以通过在程序中导入 java.util.stream包来使用流。

Stream提供以下功能:

Stream不存储元素。它只是通过计算操作的管道传送来自数据结构、数组或 I/O 通道等源的元素。

Stream本质上是函数式的,对流执行的操作不会修改其源。例如,过滤从集合获取的 Stream 会生成一个没有过滤元素的新 Stream,而不是从源集合中删除元素。

Stream是惰性的,仅在需要时才计算代码,在流的生命周期中,流的元素仅被访问一次。

与迭代器一样,必须生成新流才能重新访问源中的相同元素。 您可以使用 Stream 来 过滤、收集、打印以及 从一种数据结构转换为其他数据结构等。

Stream API 示例

1. 创建一个空的Stream

在创建空流时,应使用 empty() 方法:

java 复制代码
Stream<String> stream = Stream.empty();
stream.forEach(System.out::println);

通常情况下,在创建时会使用 empty() 方法,以避免在没有元素的流中返回 null:

java 复制代码
public Stream<String> streamOf(List<String> list) {
    return list == null || list.isEmpty() ? Stream.empty() : list.stream();
}

2.从集合中创建流

java 复制代码
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

public class StreamCreationExamples {
    public static void main(String[] args) throws IOException {

        Collection<String> collection = Arrays.asList("JAVA", "J2EE", "Spring", "Hibernate");
        Stream<String> stream2 = collection.stream();
        stream2.forEach(System.out::println);

        List<String> list = Arrays.asList("JAVA", "J2EE", "Spring", "Hibernate");
        Stream<String> stream3 = list.stream();
        stream3.forEach(System.out::println);

        Set<String> set = new HashSet<>(list);
        Stream<String> stream4 = set.stream();
        stream4.forEach(System.out::println);
    }
}

输出

复制代码
JAVA
J2EE
Spring
Hibernate
JAVA
J2EE
Spring
Hibernate
JAVA
Hibernate
J2EE
Spring

3. 从数组中创建流对象

数组可以是流的源,也可以从现有数组或数组的一部分创建数组:

java 复制代码
import java.util.Arrays;
import java.util.stream.Stream;

public class StreamCreationExample {
    public static void main(String[] args) {
        // 使用Arrays.stream()创建流
        int[] numbers = {1, 2, 3, 4, 5};
        Stream<Integer> stream1 = Arrays.stream(numbers);
        System.out.println("Using Arrays.stream():");
        stream1.forEach(System.out::println);

        // 使用Stream.of()创建流
        String[] names = {"Alice", "Bob", "Charlie"};
        Stream<String> stream2 = Stream.of(names);
        System.out.println("Using Stream.of():");
        stream2.forEach(System.out::println);

        // 使用Stream.builder()创建流
        String[] colors = {"Red", "Green", "Blue"};
        Stream.Builder<String> builder = Stream.builder();
        for (String color : colors) {
            builder.add(color);
        }
        Stream<String> stream3 = builder.build();
        System.out.println("Using Stream.builder():");
        stream3.forEach(System.out::println);
    }
}

输出

scss 复制代码
Using Arrays.stream():
1
2
3
4
5
Using Stream.of():
Alice
Bob
Charlie
Using Stream.builder():
Red
Green
Blue

4. 使用Stream过滤一个集合示例

在下面的示例中,我们不使用流过滤数据,看看代码是什么样的,同时我们在给出一个使用stream过滤的示例,对比一下

不使用Stream过滤一个集合示例

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class FilterWithoutStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        numbers.add(40);
        numbers.add(50);

        List<Integer> filteredNumbers = new ArrayList<>();
        for (Integer number : numbers) {
            if (number > 30) {
                filteredNumbers.add(number);
            }
        }

        System.out.println("Filtered numbers (without Stream):");
        for (Integer number : filteredNumbers) {
            System.out.println(number);
        }
    }
}

输出:

java 复制代码
Filtered numbers (without Stream):
40
50

使用 Stream 过滤集合示例:

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class FilterWithStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        numbers.add(40);
        numbers.add(50);

        List<Integer> filteredNumbers = numbers.stream()
                .filter(number -> number > 30)
                .toList();

        System.out.println("Filtered numbers (with Stream):");
        filteredNumbers.forEach(System.out::println);
    }
}

输出:

java 复制代码
Filtered numbers (with Stream):
40
50

前后我们对比一下,可以看到,使用 Stream 进行集合过滤可以更加简洁和直观,减少了手动迭代和添加元素的步骤。它提供了一种声明式的编程风格,使代码更易读、可维护和可扩展。

5. 使用Stream过滤和遍历集合

在下面的示例中,我们使用 filter() 方法进行过滤,使用 forEach() 方法对数据流进行迭代:

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class FilterAndIterateWithStreamExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Eve");

        System.out.println("Filtered names starting with 'A':");
        names.stream()
                .filter(name -> name.startsWith("A"))
                .forEach(System.out::println);
    }
}

输出

java 复制代码
Filtered names starting with 'A':
Alice

在上述示例中,我们有一个字符串列表 names,其中包含了一些名字。 我们使用 Stream 进行过滤和迭代操作以查找以字母 "A" 开头的名字。

6.使用Collectors方法求和

我们还可以使用Collectors计算数值之和。

在下面的示例中,我们使用Collectors类及其指定方法计算所有产品价格的总和。

java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class SumByUsingCollectorsMethods {
    public static void main(String[] args) {
        List < Product > productsList = new ArrayList < Product > ();
        productsList.add(new Product(1, "HP Laptop", 25000f));
        productsList.add(new Product(2, "Dell Laptop", 30000f));
        productsList.add(new Product(3, "Lenevo Laptop", 28000f));
        productsList.add(new Product(4, "Sony Laptop", 28000f));
        productsList.add(new Product(5, "Apple Laptop", 90000f));
        
        double totalPrice3 = productsList.stream()
            .collect(Collectors.summingDouble(product -> product.getPrice()));
        System.out.println(totalPrice3);

    }
}

输出

复制代码
201000.0

7. 使用Stream查找年龄最大和最小的学生

假设有一个 Student 类具有 name 和 age 属性。我们可以使用 Stream 来查找学生集合中年龄的最大和最小值,并打印出相应的学生信息。以下是一个示例:

java 复制代码
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

public class StudentStreamExample {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 20));
        students.add(new Student("Bob", 22));
        students.add(new Student("Charlie", 19));
        students.add(new Student("David", 21));

        // 查找年龄最大的学生
        Optional<Student> maxAgeStudent = students.stream()
                .max(Comparator.comparingInt(Student::getAge));

        // 查找年龄最小的学生
        Optional<Student> minAgeStudent = students.stream()
                .min(Comparator.comparingInt(Student::getAge));

        // 打印最大和最小年龄的学生信息
        System.out.println("Student with maximum age:");
        maxAgeStudent.ifPresent(System.out::println);

        System.out.println("Student with minimum age:");
        minAgeStudent.ifPresent(System.out::println);
    }
}

class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

输出:

ini 复制代码
Student with maximum age:
Student{name='Bob', age=22}
Student with minimum age:
Student{name='Charlie', age=19}

8. 使用Stream转换List为Map

java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class StudentStreamToMapExample {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 20));
        students.add(new Student("Bob", 22));
        students.add(new Student("Charlie", 19));
        students.add(new Student("David", 21));

        // 将学生列表转换为 Map,以姓名为键,学生对象为值
        Map<String, Student> studentMap = students.stream()
                .collect(Collectors.toMap(Student::getName, student -> student));

        // 打印学生 Map
        for (Map.Entry<String, Student> entry : studentMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

输出

ini 复制代码
David: Student{name='David', age=21}
Bob: Student{name='Bob', age=22}
Charlie: Student{name='Charlie', age=19}
Alice: Student{name='Alice', age=20}

在上面示例中,我们使用Collectors.toMap() 方法将学生列表转换为 Map。我们指定了键提取器 Student::getName,将学生的姓名作为键。对于值提取器,我们使用了一个匿名函数 student -> student,它返回学生对象本身作为值。

9. 使用Stream把List对象转换为另一个List对象

假设我们有一个 Person 类,其中包含姓名和年龄属性。我们可以使用 Stream 来将一个 List 对象转换为另一个 List 对象,其中只包含人员的姓名。以下是一个示例:

java 复制代码
  import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class ListTransformationExample {
    public static void main(String[] args) {
        List<Person> persons = new ArrayList<>();
        persons.add(new Person("Alice", 20));
        persons.add(new Person("Bob", 22));
        persons.add(new Person("Charlie", 19));

        // 将 Person 列表转换为只包含姓名的 String 列表
        List<String> names = persons.stream()
                .map(Person::getName)
                .collect(Collectors.toList());

        // 打印转换后的姓名列表
        System.out.println(names);
    }
}

class Person {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

输出:

csharp 复制代码
  [Alice, Bob, Charlie]

在上述示例中,我们有一个 Person 类,其中包含姓名和年龄属性。我们创建了一个 persons 列表,并添加了几个 Person 对象。

使用Stream,我们通过调用 map() 方法并传入一个方法引用 Person::getName,最后,我们使用 collect() 方法和 Collectors.toList() 将转换后的姓名收集到一个新的列表中。

API

docs.oracle.com/javase/8/do...

相关推荐
扬大平仔1 小时前
# 小深:用 AgentScope Java 2.0 Harness 做私人助手(上)mysql
java·开发语言·mysql
java porter1 小时前
我开源了一个 Spring AI Agent 项目
后端
阿里云基础软件2 小时前
一句话看透 JVM,SysOM 诊断 Skill 新增 Java 应用诊断能力
java·开发语言·jvm·人工智能·操作系统·sysom 诊断 skill
Wang's Blog2 小时前
Java框架快速入门: Spring Security+OAuth2之自动化集成测试
java·spring·自动化
136096757232 小时前
AgentScope 2.0 学习笔记:RAG 进阶——Top-K 调优与幻觉测试(让 Agent 敢说「不知道」)
后端
yume_sibai2 小时前
07-Rust 异步编程完全指南(async/await + Tokio + Future + 并发原语 + 异步流)
开发语言·后端·rust
Sylvia33.2 小时前
板球实时数据接入实战:从Frames模型到多赛制适配
java·python·websocket·网络协议·架构
Zzzzmo_2 小时前
SpringBoot 日志
java·spring boot·spring
2601_953988072 小时前
Ricon组态 - 让数据可视化如此简单
运维·后端·物联网·数学建模·前端框架·c4前端
hai_android2 小时前
Android JNI 示例详解:Java 与 C++ 互调演示
android·java