# Java教程 - Java8新特性

Java 8 (又称为jdk 1.8) 是Java 语言开发的一个主要版本,是 Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。

下面介绍一下 Java8 常用的新特性。

# 1.1 接口的默认方法与静态方法

以前的版本定义接口是不能有实现机制的,现在可以使用 default 关键字声明默认方法,然后子类可以重写,也可以直接使用了。另外接口中也可以声明静态方法并提供方法的实现。以后的工具类就可以参考接口来设计,例如大量的方法被添加到 java.util.Collection 接口中去,例如 stream(),parallelStream(),forEach(),removeIf()等。

声明默认方法和静态方法:

interface FirstInterface {
    // 方法和常量,默认就是public的
    // 不写final static,默认就是常量
    String key = "abc";

    /**
     * 定义默认方法
     */
    default void commomMethod() {
        System.out.println(key);
    }

    /**
     * 定义静态方法
     */
    static void firstMethod(int value) {
        System.out.println(key + value);
    }
}

//实现
public class InterfaceTest implements FirstInterface {
    public static void main(String[] args) {
        //调用静态方法
        FirstInterface.firstMethod(123);

        InterfaceTest test = new InterfaceTest();
        test.commomMethod();

    }

    /**
     * 重写默认方法
     */
    @Override
    public void commomMethod() {
        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

但是需要注意不能用默认方法来重写 equals()hashCode()toString(),接口不能提供对 Object 类的任何方法的默认实现。

# 1.2 Lambda表达式

在讲 Lambda 表达式之前需要先说函数式接口。

# 1 函数式接口

什么是函数式接口?

函数式接口就是只包含一个方法的接口。比如 Java 标准库中的 java.lang.Runnablejava.util.Comparator 都是典型的函数式接口。java 8提供 @FunctionalInterface 作为注解,这个注解是非必须的,只要接口符合函数式接口的标准(即只包含一个方法的接口),虚拟机会自动判断, 但最好在接口上使用注解@FunctionalInterface 进行声明,以免团队的其他人员错误地往接口中添加新的方法。因为默认方法不算抽象方法,所以你也可以给你的函数式接口添加默认方法。


举个栗子:

在 Java8 版本之前对集合进行排序的话,代码如下:

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class LambdaTest {
    public static void main(String[] args) {
        // 创建集合
        List<String> nameList = Arrays.asList("doubi", "niubi", "shabi", "erbi");
        
        // 排序
        Collections.sort(nameList, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.compareTo(a);
            }
        });

        System.out.println(nameList);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

在 Java8 中就没必要使用这种传统的匿名对象的方式了,Java8 可以使用 lambda 表达式,lambda 表达式就是函数式接口里面方法的实现,即是 Comparator 接口 compare 的实现。

public class LambdaTest {
    public static void main(String[] args) {
        // 创建集合
        List<String> nameList = Arrays.asList("doubi", "niubi", "shabi", "erbi");

        // 排序
        Collections.sort(nameList, (String a, String b) -> {
            return b.compareTo(a);
        });

        System.out.println(nameList);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

可以将形式参数类型省略:

// 排序
Collections.sort(nameList, (a, b) -> {
    return b.compareTo(a);
});
1
2
3
4

因为 Lambda 表达式的方法体只有一行代码,所以可以省略 {}return

// 排序
Collections.sort(nameList, (a, b) -> b.compareTo(a));
1
2

Java中的 Lambda 无法单独出现,它需要一个函数式接口,Lambda 表达式的本质是函数式接口的实例,是对象,这和其他语言的 Lambda 有所区别,其他语言的 Lambda表达式是一个函数(方法)。

# 2 Lambda表达式的语法

表达式包含三个部分:

  1. 形参列表:一个括号内用逗号分隔的形式参数,参数是 函数式接口 里面方法的参数;参数类型可以省略;如果形参只有一个参数,形参的括号 () 可以省略;
  2. 一个箭头符号:->
  3. 方法体:可以是表达式或代码块,方法体是函数式接口里面方法的实现。如果是代码块,则必须用 {}来包裹起来。如果只是一行表达式,那么可以省略 return{}

下面举一些例子:

// 1. 不需要参数,返回值为 5  
() -> {return 5;}
// 或
() -> 5

// 2. 接收一个参数(数字类型),返回其2倍的值  
(int x) -> {return 2 * x;}
// 或
(x) -> 2 * x
// 或
x -> 2 * x

// 3. 接收2个int型整数,返回他们的和  
(int x, int y) -> {return x + y;}
// 或
(int x, int y) -> x + y
// 或
(x, y) -> x + y

// 4. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) 
(String s) -> {System.out.print(s);}
// 或
(s) -> System.out.print(s)
// 或
s -> System.out.print(s)
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

Lambda 表达式需要与函数式接口的实现对应。

# 3 自定义函数式接口

定义一个函数式接口,只包含一个抽象方法:

建议添加 @FunctionalInterface 注解。

@FunctionalInterface
interface MyLambdaInterface {
    int toInt(String arg);
}
1
2
3
4

使用 Lambda 表达式匹配函数式接口:

public class LambdaTest {
    public static void main(String[] args) {
        // 在Java8之前的版本
        testMethod("123", new MyLambdaInterface() {
            @Override
            public int toInt(String arg) {
                return Integer.parseInt(arg);
            }
        });

        // 使用 Lambda表达式
        testMethod("123", arg -> Integer.parseInt(arg));
    }

    public static void testMethod(String strArg, MyLambdaInterface lambdaInterface) {
        int i = lambdaInterface.toInt(strArg);
        System.out.println(i);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 1.3 方法引用

当 Lambda 方法体的操作已经有现成可以使用的方法了,就可以使用方法引用。方法引用是 Lambda 表达式的一个简化写法,所引用的方法其实是 Lambda 表达式的方法体实现,语法也很简单,左边是类名或对象,中间是 :: ,右边是相应的方法名。

方法引用可以分为以下几种形式:

  • 如果是静态方法,使用 类名::方法名
  • 如果是实例方法,使用 类名::方法名对象::方法名
  • 构造函数,使用 ClassName::new

举个栗子,继续用上面的例子。

首先是一个函数式接口:

@FunctionalInterface
interface MyLambdaInterface {
    int toInt(String arg);
}
1
2
3
4

使用方法引用 :

public class LambdaTest {
    public static void main(String[] args) {
        // 使用Lambda表达式
        testMethod("123", arg -> Integer.parseInt(arg));

        // 使用方法引用
        testMethod("123", Integer::parseInt);
    }

    public static void testMethod(String strArg, MyLambdaInterface lambdaInterface) {
        int i = lambdaInterface.toInt(strArg);
        System.out.println(i);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

如果函数式接口中的抽象方法的形参和返回值 和 方法引用的方法的参数和返回值一致,就可以使用方法引用了。

上面 int toInt(String arg)Integer.parseInt(arg) 形参和返回值相同,所以可以使用方法引用。


再举个例子,遍历集合:

public class LambdaTest {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("doubi", "niubi", "shabi");

        // 使用Lambda表达式,遍历输出列表中的元素
        list.forEach((item) -> System.out.println(item));

        // 使用方法引用,遍历输出列表中的元素
        list.forEach(System.out::println); // 输出列表中的元素
    }
}
1
2
3
4
5
6
7
8
9
10
11

forEach() 方法的参数是 Consumer 接口的实现,Consumer 接口是一个函数式接口,接口中的抽象方法是 void accept(T t); ,参数和返回值与 System.out.println() 方法参数和返回值兼容,所以可以使用方法引用。


再举个例子:

@FunctionalInterface
interface EqualsInterface {
    boolean eq(String s1, String s2);
}

public class LambdaTest {
    public static void main(String[] args) {

        // 使用Lambda表达式
        testMethod("abc", "abc", (s1, s2) -> s1.equals(s2));

        // 使用方法引用
        testMethod("abc", "abc", String::equals);
    }

    public static void testMethod(String str1, String str2, EqualsInterface equalsInterface) {
        boolean equals = equalsInterface.eq(str1, str2);
        System.out.println(equals);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

上面的函数式接口中的参数和 String 类中的 equals() 方法参数不一致,但是也是可以使用方法引用,有一个参数作为调用对象。


再举个构造方法的例子:

class Student {
    private String name;

    public Student() {
    }

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

@FunctionalInterface
interface GenerateInterface {
    // 返回一个Student对象
    Student generate(String name);
}

public class LambdaTest {
    public static void main(String[] args) {
        // 使用Lambda表达式
        testMethod("doubibiji", name -> new Student(name));

        // 使用方法引用
        testMethod("abc", Student::new);
    }

    public static void testMethod(String name, GenerateInterface equalsInterface) {
        Student stu = equalsInterface.generate(name);
        System.out.println(stu);
    }
}
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

上面函数式接口返回的是一个 Student对象,那么在实现函数式接口的时候,就可以使用构造方法的方法引用。

# 1.4 Stream

Stream 是用于处理集合数据的流式操作。它提供了一种更便利、高效的方式来处理集合数据。

什么是 Stream?

  • Stream 是一种数据流:Stream不会存储元素,它不是数据结构,而是一种用于操作集合数据的流式处理机制。
  • Stream 提供了丰富的操作方法:可以进行过滤、映射、排序、聚合等操作。
  • Stream 是惰性求值的:只有在需要结果时才会执行操作,可以提高性能。

Stream的特点:

  • 链式操作:可以将多个操作连接起来形成一个流水线,提高代码可读性。
  • 不修改原始数据:Stream 操作不会修改原始集合数据,而是生成新的 Stream。

先举个栗子:

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

        // 获取Stream
        Stream<Integer> stream = numbers.stream();

        // 遍历输出集合中的偶数,然后转换为新的集合
        List<Integer> newList = stream.filter(n -> n % 2 == 0).toList();
        System.out.println(newList);// 输出结果: 2, 4, 6

        //stream.filter(n -> n > 5);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

上面首先获取到 Stream 对象,然后执行操作 filter,最后通过 toList() 转换为一个集合。 toList() 是一个终止操作,所以后面无法再试用 Stream。如果要使用,只能重新获取新的对象。

所以 Stream的执行流程是:

  1. Stream的实例化;
  2. 进行一系列的中间操作,例如过滤、映射、排序等;
  3. 终止操作

# 1 获取Stream

获取Stream有如下四种方式:

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

public class StreamTest {
    public static void main(String[] args) {

        // 方式一:通过集合获取
        List<String> nameList = Arrays.asList("doubi", "niubi", "shabi", "erbi");
        // 返回一个顺序流
        Stream<String> stream1 = nameList.stream();
        // 返回一个并行流
        Stream<String> stream2 = nameList.parallelStream();


        // 方式二:通过数组获取
        String[] names = new String[]{"doubi", "niubi", "shabi", "erbi"};
        Stream<String> stream3 = Arrays.stream(names);


        // 方式三:通过Stream.of获取
        Stream<String> stream4 = Stream.of("doubi", "niubi", "shabi", "erbi");


        // 方式四:创建无限流,不常用
        // 通过迭代的方式,获取10个偶数,如果没有limit,不会停止,下一个元素在上一个元素的基础上+2
        Stream.iterate(0, t -> t + 2).limit(10).forEach(num -> System.out.println(num));

        // 通过生成的方式,获取10个随机数,通过limit限制个数
        Stream.generate(() -> Math.random()).limit(10).forEach(num -> System.out.println(num));
    }
}
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

顺序流就是按照集合中的元素顺序依次处理,并行流是一种并行处理元素的流处理方式,它利用多线程同时处理元素,加快处理速度,适用于大数据量或需要并发处理的场景。

# 2 中间操作

Stream常用的中间操作主要分为三类:筛选和切片、映射、排序

# 筛选和切片

# filter

作用:根据给定的条件过滤流中的元素。

举个栗子:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

Stream<Integer> stream = numbers.stream();

// 遍历输出集合中的偶数
stream.filter(n -> n % 2 == 0)
        .forEach(item -> System.out.println(item));
// 输出结果: 2, 4, 6
1
2
3
4
5
6
7
8

上面是通过 forEach 遍历了筛选的结果,也可以将筛选的结果收集成一个集合。

举个栗子:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

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

System.out.println(evenNumbers); // 输出结果为 [2, 4, 6]
1
2
3
4
5
6
7

注意:上面的 forEachcollect 方法都是终止函数,也就是后面无法再跟其他函数了,而且获取到的 stream 也无法继续使用了,如果想再使用,只能重新获取 Stream。

# limit和skip

作用

  • limit(n):截断流,使其最多只包含前 n 个元素。
  • skip(n):跳过流中的前 n 个元素,返回剩余的元素。

举个栗子:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// 只获取5个
List<Integer> limitedNumbers = numbers.stream()
        .limit(5)
        .collect(Collectors.toList());

System.out.println(limitedNumbers);// 输出结果为 [1, 2, 3, 4, 5]

// 跳过前面5个
List<Integer> skippedNumbers = numbers.stream()
        .skip(5)
        .collect(Collectors.toList());

System.out.println(skippedNumbers);// 输出结果为 [6, 7, 8, 9, 10]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# distinct

作用:去除流中重复的元素,就是去重。

举个栗子:

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 4, 4);

// 去重
List<Integer> distinctNumbers = numbers.stream()
        .distinct()
        .collect(Collectors.toList());

System.out.println(distinctNumbers);// 输出结果为 [1, 2, 3, 4]
1
2
3
4
5
6
7
8

如果是对象元素,根据 hashCode()equals() 去除重复元素。

# 映射

# map

作用:将流中的每个元素转换映射为另一个元素。

举个栗子:

List<String> words = Arrays.asList("apple", "banana", "cherry");

// 获取集合中元素的长度,组成另一个集合
List<Integer> wordLengths = words.stream()
        .map(String::length)
        .collect(Collectors.toList());

System.out.println(wordLengths);// 输出结果为 [5, 6, 6]
1
2
3
4
5
6
7
8

再举个例子:

获取学生成绩在90分以上的学生姓名:

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

class Student {
    private String name;
    private int score;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}


public class StreamTest {
    public static void main(String[] args) {
        List<Student> stuList = new ArrayList<>();
        stuList.add(new Student("doubi", 80));
        stuList.add(new Student("erbi", 93));
        stuList.add(new Student("niubi", 98));
        stuList.add(new Student("shibi", 79));
        stuList.add(new Student("shabi", 62));

        List<String> nameList = stuList.stream().filter(stu -> stu.getScore() > 90).map((stu) -> stu.getName()).collect(Collectors.toList());
        System.out.println(nameList);
    }
}
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
# flatMap

作用:将流中的每个元素映射为一个流,然后将这些流连接成一个流。

举个栗子:

List<List<Integer>> numbers = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6));

// 将多个集合合成一个结合
List<Integer> flattenedNumbers = numbers.stream()
        .flatMap(Collection::stream)
        .collect(Collectors.toList());
System.out.println(flattenedNumbers); // 输出结果为 [1, 2, 3, 4, 5, 6]
1
2
3
4
5
6
7
# peek

作用:对流中的每个元素执行操作,返回流本身。

举个栗子:

List<String> words = Arrays.asList("apple", "banana", "cherry");
List<String> modifiedWords = words.stream()
        .peek(word -> System.out.println("Processing: " + word))
        .map(String::toUpperCase)
        .collect(Collectors.toList());

System.out.println(modifiedWords);

// 输出结果为:
// Processing: apple
// Processing: banana
// Processing: cherry
// [APPLE, BANANA, CHERRY]
1
2
3
4
5
6
7
8
9
10
11
12
13

# 排序

# sorted

作用:对流中的元素进行排序。

举个栗子:

自然排序:

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
List<Integer> sortedNumbers = numbers.stream()
        .sorted()
        .collect(Collectors.toList());

System.out.println(sortedNumbers); // 输出结果为 [1, 1, 2, 3, 4, 5, 6, 9]
1
2
3
4
5
6

定制排序,传入 Comparator 接口实例:

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
List<Integer> sortedNumbers = numbers.stream()
        .sorted((o1, o2) -> Integer.compare(o1, o2) * -1)   // *-1取倒序
        .collect(Collectors.toList());

System.out.println(sortedNumbers); // 输出结果为 [9, 6, 5, 4, 3, 2, 1, 1]
1
2
3
4
5
6

# 3 终止操作

终止操作是指会触发流的处理并产生一个结果的操作。这些操作会消耗流中的数据,并且一旦执行了终止操作,流就不能再被使用了。终止操作是流处理流程中的最后一步,它们将中间操作的结果转换为某种形式的汇总数据(如列表、集合、数组、单个值等)。

常用的终止操作有如下类型:查找和匹配、归约、收集和遍历。

# 查找和匹配

# allMatch

作用:检查是否所有元素都匹配给定的条件。

List<String> list = Arrays.asList("apple", "banana", "cherry");

// 是否所有的水果都以a开头
boolean allFruitsStartWithA = list.stream()
        .allMatch(fruit -> fruit.startsWith("a"));

System.out.println(allFruitsStartWithA);// false
1
2
3
4
5
6
7
# anyMatch

作用:检查是否至少有一个元素匹配给定的条件。

举个栗子:

List<String> list = Arrays.asList("apple", "banana", "cherry");
        
// 是否有一个水果以a开头
boolean hasFruitStartingWithA = list.stream()
        .anyMatch(fruit -> fruit.startsWith("a")); 

System.out.println(hasFruitStartingWithA);// true
1
2
3
4
5
6
7
# noneMatch

作用:检查是否所有的元素都不满足给定的条件。

举个栗子:

List<String> list = Arrays.asList("banana", "orange", "kiwi");

// 是否所有的水果都不以a开头
boolean noFruitsStartWithA = list.stream()
        .noneMatch(fruit -> fruit.startsWith("a")); // true

System.out.println(noFruitsStartWithA);// true
1
2
3
4
5
6
7
# findFirst

作用:

举个栗子:

List<String> list = Arrays.asList("apple", "banana", "cherry");

// 获取第一个元素
Optional<String> firstFruit = list.stream()
        .findFirst();

String fruit = firstFruit.get();
System.out.println(fruit);  // apple
1
2
3
4
5
6
7
8
# findAny

作用:返回流中的任意元素

举个栗子:

List<String> list = Arrays.asList("apple", "banana", "cherry");

Optional<String> anyFruit = list.stream().findAny();
String fruit = anyFruit.get();
System.out.println(fruit);// 可能返回"apple"、"banana"或"cherry"中的任意一个
1
2
3
4
5

# 归约和汇总

# count

作用:返回流中元素的个数。

举个栗子:

List<String> list = Arrays.asList("apple", "banana", "cherry");

long count = list.stream().count(); 

System.out.println(count);// 3
1
2
3
4
5
# max和min

作用:计算流中元素的最大值和最小值。

举个栗子:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 方式1:求最大值
Optional<Integer> max1 = numbers.stream()
        .max(Integer::compare);
System.out.println(max1.get());  // 5

// 方式2:求最大值
OptionalInt max2 = numbers.stream()
        .mapToInt(Integer::intValue)
        .max();
System.out.println(max2.getAsInt());  // 5

// 方式1:求最小值
Optional<Integer> min1 = numbers.stream()
        .min(Integer::compare);
System.out.println(min1.get());  // 1

// 方式2:求最小值
OptionalInt min2 = numbers.stream()
        .mapToInt(Integer::intValue)
        .min();
System.out.println(min2.getAsInt());  // 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# sum和average

作用:计算流中元素的和和平均值。

举个栗子:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 求和
int sum = numbers.stream()
        .mapToInt(Integer::intValue)
        .sum(); 
System.out.println(sum);   // 15

// 求平均值
OptionalDouble average = numbers.stream()
        .mapToDouble(Integer::doubleValue)
        .average();
System.out.println(average.getAsDouble());  // 3.0
1
2
3
4
5
6
7
8
9
10
11
12
13
# reduce

作用:将流中的元素结合起来,得到一个值。

举个栗子:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);  
Integer sum = numbers.stream()  
    .reduce(0, Integer::sum); // 15
1
2
3

# 收集

# collect

作用:将流中的元素收集到一个新的集合中(如List、Set、Map等)。

举个栗子:

List<String> list = Arrays.asList("apple", "banana", "cherry");

List<String> upperCaseFruits = list.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());

System.out.println(upperCaseFruits);  // ["APPLE", "BANANA", "CHERRY"]
1
2
3
4
5
6
7

CollectorstoList()toSet()toMap() 等方法。


还可以直接使用 toList()toArray() 方法:

List<String> list = Arrays.asList("apple", "banana", "cherry");

List<String> upperCaseFruitList = list.stream()
        .map(String::toUpperCase)
        .toList();

String[] upperCaseFruitArray = list.stream()
        .map(String::toUpperCase)
        .toArray(String[]::new);

System.out.println(upperCaseFruitList);  // ["APPLE", "BANANA", "CHERRY"]
System.out.println(Arrays.toString(upperCaseFruitArray));  // ["APPLE", "BANANA", "CHERRY"]
1
2
3
4
5
6
7
8
9
10
11
12

# 遍历

# forEach

作用:对流中的每个元素执行某个操作。

举个栗子:

List<String> list = Arrays.asList("apple", "banana", "cherry");
list.stream()
  .forEach(fruit -> System.out.println(fruit)); // 打印所有水果
1
2
3
# forEachOrdered

作用:对于有序流,按照流中元素的顺序,对每个元素执行某个操作。

举个栗子:

List<String> list = Arrays.asList("apple", "banana", "cherry");  
list.stream()  
    .forEachOrdered(fruit -> System.out.println(fruit)); // 按照顺序打印所有水果
1
2
3

注意:对于顺序流,forEachforEachOrdered的行为是相同的。但如果你在处理一个并行流,并且关心元素的顺序,那么应该使用forEachOrdered