【Java】Java 8 Stream API

文章目录

概念

Java 8 引入了 Stream API,这是一个用于处理集合的强大工具。Stream API 提供了一种声明性的方法来处理数据集合,使代码更简洁、易读且高效。Stream 是对数据源(如集合、数组等)元素的序列化抽象,支持许多操作,如过滤、排序、映射等。

Stream API 的基本概念:

  • Stream(流):是一个来自某种数据源的元素队列,它支持连续的管道式操作。
  • Source(数据源):可以是一个集合(Collection)、数组(Array)或其他任何提供数据的源头。
  • Intermediate Operations(中间操作):一系列可以连接起来的操作,比如 filter、map、flatMap 等,这些操作不会执行任何处理,而是生成一个新的流。
  • Terminal Operation(终端操作):会触发之前所有的中间操作,并且产生结果,如 collect、forEach、reduce 等。
  • Pipeline(流水线):由一个 Stream、零个或多个中间操作以及一个终端操作组成的一系列操作。

Stream API 的主要特性:

  • Lambda 表达式:Stream API 的操作需要使用 Lambda 表达式来定义如何处理每个元素。
  • 内部迭代:与传统的外部迭代(如 for-each 循环)不同,Stream API 使用内部迭代,即 Stream API 自己管理迭代逻辑。
  • 惰性求值:大多数 Stream 操作都是惰性的,只有当执行终端操作时才会触发整个流水线的执行。
  • 并行流:可以很容易地利用多核处理器的优势,通过并行流(parallel stream)来提高数据处理的性能。

示例

1.创建Stream
java 复制代码
// 从集合创建 Stream
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> streamFromList = list.stream();

// 从数组创建 Stream
String[] array = {"a", "b", "c"};
Stream<String> streamFromArray = Arrays.stream(array);

// 使用 Stream.of() 方法创建 Stream
Stream<String> streamOf = Stream.of("a", "b", "c");
2.中间操作
java 复制代码
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class IntermediateOperationsExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c", "d", "e", "a", "b");

        // 过滤操作:只保留字符串长度大于1的元素
        List<String> filteredList = list.stream()
                .filter(s -> s.length() > 1)
                .collect(Collectors.toList());

        // 映射操作:将每个字符串转换为大写
        List<String> mappedList = list.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());

        // 排序操作:按字母顺序排序
        List<String> sortedList = list.stream()
                .sorted()
                .collect(Collectors.toList());

        // 去重操作:移除重复元素
        List<String> distinctList = list.stream()
                .distinct()
                .collect(Collectors.toList());

        // 限制操作:只保留前3个元素
        List<String> limitedList = list.stream()
                .limit(3)
                .collect(Collectors.toList());

        // 跳过操作:跳过前2个元素
        List<String> skippedList = list.stream()
                .skip(2)
                .collect(Collectors.toList());

        // 组合操作:先去重,再转换为大写,然后排序
        List<String> combinedList = list.stream()
                .distinct()
                .map(String::toUpperCase)
                .sorted()
                .collect(Collectors.toList());

        System.out.println("Filtered List: " + filteredList); // []
        System.out.println("Mapped List: " + mappedList);     // [A, B, C, D, E, A, B]
        System.out.println("Sorted List: " + sortedList);     // [a, a, b, b, c, d, e]
        System.out.println("Distinct List: " + distinctList); // [a, b, c, d, e]
        System.out.println("Limited List: " + limitedList);   // [a, b, c]
        System.out.println("Skipped List: " + skippedList);   // [c, d, e, a, b]
        System.out.println("Combined List: " + combinedList); // [A, B, C, D, E]
    }
}

中间操作详解

  1. filter(Predicate<? super T> predicate)

filter 方法接收一个谓词(Predicate),该谓词是一个返回布尔值的函数。filter 方法会筛选出流中满足给定谓词的所有元素。在这个例子中,filter 用于只保留长度大于1的字符串。

java 复制代码
.filter(s -> s.length() > 1)
  1. map(Function<? super T, ? extends U> mapper)

map 方法接收一个函数(Function),该函数将流中的每个元素转换成另一种形式。在这个例子中,map 方法将每个字符串转换为其大写形式。

java 复制代码
.map(String::toUpperCase)
  1. sorted()

sorted 方法对流中的元素进行自然排序,或者你可以传递一个自定义的比较器(Comparator)。在这个例子中,sorted 用于按照字母顺序对字符串进行排序。

java 复制代码
.sorted()
  1. distinct()

distinct 方法用于去除流中的重复元素。它假设流中的元素实现了 equals() 方法。在这个例子中,distinct 用于移除列表中的重复项。

java 复制代码
.distinct()
  1. limit(long maxSize)

limit 方法用于截断流,使其元素不超过给定数量。在这个例子中,limit 用于仅保留前三个元素。

java 复制代码
.limit(3)
  1. skip(long n)

skip 方法用于跳过流中的前n个元素。在这个例子中,skip 用于跳过前两个元素。

java 复制代码
.skip(2)
  1. 组合操作

可以将多个中间操作组合在一起,形成一个操作链。例如,先去重,再转换为大写,然后排序。

java 复制代码
List<String> combinedList = list.stream()
        .distinct()
        .map(String::toUpperCase)
        .sorted()
        .collect(Collectors.toList());
3.终端操作示例

终端操作会触发 Stream 的处理,并返回一个非 Stream 的结果。

java 复制代码
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
public class TerminalOperationsExample {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        // forEach 操作:对每个元素执行操作
        list.stream().forEach(System.out::println);
        list.stream().forEach(n -> System.out.println(n));
        Consumer<Integer> printConsumer = n -> System.out.println(n); // Consumer接口
        list.stream().forEach(printConsumer);

        // collect 操作:将 Stream 转换为 List
        List<Integer> collectedList = list.stream().collect(Collectors.toList());
        // collect 操作:将 Stream 转换为 Set
        Set<Integer> collectedSet = list.stream().collect(Collectors.toSet());

        // reduce 操作:将元素组合成一个值
        Optional<Integer> sum = list.stream().reduce(Integer::sum);
        sum.ifPresent(System.out::println); // 15

        // count 操作:计算元素数量
        long count = list.stream().count();
        System.out.println(count); // 5

        // findFirst 操作:获取第一个元素
        Optional<Integer> first = list.stream().findFirst();
        first.ifPresent(f -> System.out.println("First element: " + f)); // 1

        // findAny 操作:获取任意一个元素
        Optional<Integer> any = list.stream().findAny();
        any.ifPresent(a -> System.out.println("Any element: " + a)); // 1 (在顺序流中)

        // max 操作:获取最大元素
        Optional<Integer> max = list.stream().max(Comparator.naturalOrder());
        max.ifPresent(m -> System.out.println("Max element: " + m)); // 5

        // min 操作:获取最小元素
        Optional<Integer> min = list.stream().min(Comparator.naturalOrder());
        min.ifPresent(m -> System.out.println("Min element: " + m)); // 1
    }
}
4.并行 Stream

Stream API 还支持并行处理,可以利用多核处理器提高性能。

java 复制代码
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 使用并行 Stream 处理数据
list.parallelStream().forEach(System.out::println);

实战

1. 在一个存有学生的List中,找到年龄小于20的学生姓名。
java 复制代码
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class Student {
    private String name;
    private int age;
    // 省略构造方法,set/get方法等
}

public class StreamExample {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Alice", 22),
                new Student("Bob", 19),
                new Student("Charlie", 18),
                new Student("David", 21),
                new Student("Eve", 17)
        );

        // 使用Stream API找到年龄小于20的学生姓名
        List<String> names = students.stream()
                .filter(student -> student.getAge() < 20) // 过滤年龄小于20的学生
                .map(Student::getName) // 映射到学生姓名
                .collect(Collectors.toList()); // 收集结果到List
    }
}
2.在一个存有学生的List中,找到年龄小于20的女生学生,设置其学号为24+序号
java 复制代码
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

class Student {
    private String name;   // 姓名
    private int age;       // 年龄
    private String gender; // 性别
    private int studentId; //学号
    // 省略构造/set/get等方法
}

public class StudentIdSetter {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Alice", 18, "Female"),
                new Student("Bob", 19, "Male"),
                new Student("Charlie", 18, "Male"),
                new Student("David", 20, "Male"),
                new Student("Eve", 17, "Female"),
                new Student("Fiona", 18, "Female"),
                new Student("George", 21, "Male"),
                new Student("Hannah", 19, "Female")
        );

        // 过滤出年龄小于20的女生学生
        List<Student> filteredStudents = students.stream()
                .filter(s -> s.getAge() < 20 && "Female".equals(s.getGender()))
                .collect(Collectors.toList());

        // 设置学号为24拼接序号
        IntStream.range(0, filteredStudents.size()).forEach(i -> {
            String studentId = "24" + String.format("%03d", i + 1);
            filteredStudents.get(i).setStudentId(studentId);
        });

        // 输出结果
        System.out.println("Students with updated IDs:");
        filteredStudents.forEach(System.out::println);
    }
}
/*
Students with updated IDs:
Student{name='Alice', age=18, gender='Female', studentId='24001'}
Student{name='Eve', age=17, gender='Female', studentId='24002'}
Student{name='Fiona', age=18, gender='Female', studentId='24003'}
Student{name='Hannah', age=19, gender='Female', studentId='24004'}
*/
3.在所有学生中,找到是否存在年龄小于20的,如果有则获取该学生,没有则其他操作
java 复制代码
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

class Student {
    private String name;
    private int age;
    private String gender;
	// 构造方法等
}

public class StudentCheck {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Alice", 18, "Female"),
                new Student("Bob", 19, "Male"),
                new Student("Charlie", 18, "Male"),
                new Student("David", 20, "Male"),
                new Student("Eve", 17, "Female"),
                new Student("Fiona", 18, "Female"),
                new Student("George", 21, "Male"),
                new Student("Hannah", 19, "Female")
        );

        // 检查是否存在年龄小于20的学生
        boolean exists = students.stream().anyMatch(s -> s.getAge() < 20);

        if (exists) {
            // 如果存在年龄小于20的学生,则获取第一个
            Optional<Student> studentOptional = students.stream()
                    .filter(s -> s.getAge() < 20)
                    .findFirst();

            studentOptional.ifPresent(student -> {
                // 处理找到的学生
                System.out.println("Found a student with age less than 20: " + student);
            });
        } else {
            // 如果不存在年龄小于20的学生,则执行其他操作
            System.out.println("No student with age less than 20 found.");
        }
    }
}

提问:该方法是否可优化?

4.在所有班级中,找到女生人数和男生之差小于10的班级号和班主任名字以及男、女生人数
java 复制代码
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

class Student {
    private String name;   // 姓名
    private int age;       // 年龄
    private String gender; // 性别
    // 省略构造方法,set/get方法等
}

class Class {
    private int classId;           // 班级号
    private String classTeacher;   // 班主任
    private List<Student> students;// 所有学生
    // 省略构造方法,set/get方法等
}

public class StreamExample {
    public static void main(String[] args) {
        // 1班学生
        List<Student> studentsClass1 = Arrays.asList(
                new Student("Alice", 18, "Female"),
                new Student("Bob", 19, "Male"),
                new Student("Charlie", 18, "Male"),
                new Student("David", 20, "Male"),
                new Student("Eve", 17, "Female")
        );
        // 2班学生
        List<Student> studentsClass2 = Arrays.asList(
                new Student("Fiona", 18, "Female"),
                new Student("George", 19, "Male"),
                new Student("Hannah", 18, "Female"),
                new Student("Ian", 20, "Male"),
                new Student("Jack", 17, "Male")
        );
        // 3班学生
        List<Student> studentsClass3 = Arrays.asList(
                new Student("Karen", 18, "Female"),
                new Student("Leo", 19, "Male"),
                new Student("Mona", 18, "Female"),
                new Student("Nina", 20, "Female"),
                new Student("Oscar", 17, "Male")
        );
        // 所有班级
        List<Class> classes = Arrays.asList(
                new Class(1, "Mr. Smith", studentsClass1),
                new Class(2, "Ms. Johnson", studentsClass2),
                new Class(3, "Mrs. Brown", studentsClass3)
        );

        // 使用Stream API找到女生人数和男生之差小于10的班级号和班主任名字以及男女生人数
        List<Map<String, Object>> result = classes.stream()
                .map(c -> {
                    long femaleCount = c.getStudents().stream().filter(s -> "Female".equals(s.getGender())).count();
                    long maleCount = c.getStudents().stream().filter(s -> "Male".equals(s.getGender())).count();
                    Map<String, Object> classInfo = new HashMap<>();
                    classInfo.put("classId", c.getClassId());
                    classInfo.put("classTeacher", c.getClassTeacher());
                    classInfo.put("femaleCount", femaleCount);
                    classInfo.put("maleCount", maleCount);
                    return classInfo;
                })
                .filter(ci -> Math.abs((long) ci.get("femaleCount") - (long) ci.get("maleCount")) < 10)
                .collect(Collectors.toList());

        System.out.println("Classes where the difference between the number of females and males is less than 10:");
        result.forEach(ci -> System.out.println("Class ID: " + ci.get("classId") + ", Class Teacher: " + ci.get("classTeacher") +
                ", Female Count: " + ci.get("femaleCount") + ", Male Count: " + ci.get("maleCount")));
    }
}
/*
输出:
Classes where the difference between the number of females and males is less than 10:
Class ID: 1, Class Teacher: Mr. Smith, Female Count: 2, Male Count: 3
Class ID: 2, Class Teacher: Ms. Johnson, Female Count: 3, Male Count: 2
Class ID: 3, Class Teacher: Mrs. Brown, Female Count: 3, Male Count: 2
*/
相关推荐
大数据编程之光22 分钟前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
爪哇学长36 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
ExiFengs39 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj12345678941 分钟前
JDK1.8新增特性
java·开发语言
繁依Fanyi1 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m51271 小时前
LinuxC语言
java·服务器·前端
IU宝1 小时前
C/C++内存管理
java·c语言·c++
瓜牛_gn1 小时前
依赖注入注解
java·后端·spring
hakesashou1 小时前
Python中常用的函数介绍
java·网络·python