手记

06-Stream API 操作篇

若想了解java8其它新特性,请到 00-java8常用新特性文章索引 阅读。

这篇文章写Stream API 的使用,各个用例中会大量结合 lambda 表达式,如果 函数式接口lambda表达式 玩得不熟的朋友先看看 01-函数式接口和 lambda 表达式 ,如果对 Stream API 的创建、中间操作、终止操作相关接口不熟悉的朋友先看看 05-Stream API 第一篇

提示: 表格的排版在手机端阅读可能有点乱,表格内容可以尝试手机横屏阅读或者电脑端阅读。

Java常用的内置函数式接口:

函数式接口 参数类型 返回类型 用途
Consumer 消费型接口 T void 对给定的 T 类型的参数,进行操作,
核心方法:void accept(T t);
Supplier 供给型接口 空参 T 获取一个 T 类型的对象
核心方法:T get();
Function<T, R> 函数型接口 T R 对 T 类型的参数进行操作,返回 R 类型的结果
核心方法:R apply(T t);
Predicate 断定型接口 T boolean 判断 T 类型的参数,是否满足某约束,返回 boolean 值
核心方法:boolean test(T t);
BiFunction<T, U, R> T, U R 对类型为 T, U 参数应用操作,
返回 R 类型的结果。
核心方法为 R apply(T t, U u);
UnaryOperator
(Function子接口)
T T 对类型为T的对象进行一元运算,
并返回T类型的结果。
核心方法为T apply(T t);
BinaryOperator
(BiFunction 子接口)
T, T T 对类型为T的对象进行二元运算,
并返回T类型的结果。
核心方法为T apply(T t1, T t2);
BiConsumer<T, U> T, U void 对类型为T, U 参数应用操作。
核心方法为void accept(T t, U u)
ToIntFunction
ToLongFunction
ToDoubleFunction
T int/long/double T 类型的参数,
返回int\long\double 类型的结果,
核心方法为int\long\double applyAsInt(T value);
IntFunction
LongFunction
DoubleFunction
int/long/double R 参数分别为int、long、double 类型的函数,
返回 R 类型的结果,
核心方法: R apply(int\long\double value);

1 Stream 创建 API

Stream创建:可通过 数组、Collction 和 Steam类的静态方法来创建一个Stream实例。

定义 Person ,在下面大部分测试用例用到的实体 Bean:

public 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 void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return getAge() == person.getAge() &&
                Objects.equals(getName(), person.getName());
    }

    @Override
    public int hashCode() {

        return Objects.hash(getName(), getAge());
    }

}

1.1 通过数组创建


    /**
     * 通过数组创建Stream
     */
    @Test
    public void testCreateByArray() {
        int[] nums = new int[]{0, 1, 5, 2, 4, 8, 9};
        Person[] personArray = new Person[]{
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15)
        };
        // Arrays.stream(T[] array) 将指定的数组作为数据源,返回一个顺序流
        IntStream stream = Arrays.stream(nums);
        Stream<Person> personStream = Arrays.stream(personArray);

        //打印 stream 中的元素
        System.out.println("打印 stream 中的元素: ");
        stream.forEach(item -> System.out.print(item + " "));
        //打印 personStream 中的元素
        System.out.println("\n打印 personStream 中的元素: ");
        personStream.forEach(item -> System.out.println(item + " "));
    }

运行结果:

打印 stream 中的元素: 
0 1 5 2 4 8 9 
打印 personStream 中的元素: 
Person{name='张三', age=24} 
Person{name='李四', age=29} 
Person{name='王五', age=22} 
Person{name='赵六', age=15} 

1.2 通过Collection创建

Collection接口中提供了 stream() 获取顺序流和 parallelStream() 获取并行流的两个方法
顺序流:串行流,流中的元素序列顺序和元素在数据源中的顺序是一样的。
并行流:把元素序列分成多个数据块,并用不同的线程分别处理每个数据块的流

Stream API 可以通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * 通过Collection创建Stream
     */
    @Test
    public void testCreateByCollection() {
        //stream() 将此集合作为数据源,返回一个顺序流
        Stream<Person> seqStream = persons.stream();
        //parallelStream() 将此集合作为数据源,返回一个并行流
        Stream<Person> parallelStream = persons.parallelStream();


        //打印顺序流 seqStream 中的元素
        System.out.println("打印顺序流 seqStream 中的元素: ");
        seqStream.forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));


        //并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
        // 打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的
        System.out.println("\n打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的: ");
        parallelStream.forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));

        System.out.println("\nsequential()把并行流切换成顺序流,输出: ");
        persons.parallelStream()
                .sequential()//并行流切换成顺序流
                .forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));
    }

}

运行结果:

打印顺序流 seqStream 中的元素: 
线程名main--Person{name='刘一', age=25} 
线程名main--Person{name='陈二', age=12} 
线程名main--Person{name='张三', age=24} 
线程名main--Person{name='李四', age=29} 
线程名main--Person{name='王五', age=22} 
线程名main--Person{name='赵六', age=15} 
线程名main--Person{name='孙七', age=16} 
线程名main--Person{name='周八', age=18} 

打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的: 
线程名main--Person{name='赵六', age=15} 
线程名main--Person{name='王五', age=22} 
线程名main--Person{name='周八', age=18} 
线程名main--Person{name='孙七', age=16} 
线程名ForkJoinPool.commonPool-worker-11--Person{name='李四', age=29} 
线程名ForkJoinPool.commonPool-worker-9--Person{name='张三', age=24} 
线程名ForkJoinPool.commonPool-worker-2--Person{name='陈二', age=12} 
线程名ForkJoinPool.commonPool-worker-9--Person{name='刘一', age=25} 

sequential()把并行流切换成顺序流,输出: 
线程名main--Person{name='刘一', age=25} 
线程名main--Person{name='陈二', age=12} 
线程名main--Person{name='张三', age=24} 
线程名main--Person{name='李四', age=29} 
线程名main--Person{name='王五', age=22} 
线程名main--Person{name='赵六', age=15} 
线程名main--Person{name='孙七', age=16} 
线程名main--Person{name='周八', age=18} 

1.3 通过Stream的静态方法创建Stream

Stream接口提供了几个静态方法可以创建一个Stream实例,常用的是:of()、iterate()、generate()


    /**
     * 通过Stream的静态方法创建Stream
     */
    @Test
    public void testCreateByStaticMethod() {
        // 将参数数组作为数据源,返回一个顺序流,底层调用的是Arrays.stream(T[] array)
        Stream<Integer> of = Stream.of(0, 1, 5, 2, 4, 8, 9);// 不断产生 0,2,4,6,8,10.,...,n,n+2
        System.out.println("of() 将参数数组作为数据源,返回一个顺序流: ");
        of.forEach(item -> System.out.print(item + " "));

        // 创建无限流,入参是初始元素和UnaryOperator函数式接口,返回一个有规律的无限顺序流
        Stream<Integer> iterate = Stream.iterate(0, n -> n + 2);// 不断产生 0,2,4,6,8,10.,...,n,n+2
        System.out.println("\n\niterate() 返回一个有规律的无限顺序流,限制输出前10个: ");
        iterate.limit(10).forEach(item -> System.out.print(item + " "));
        // 创建无限流,入参是Supplier,返回一个无规律的无限顺序流,其中每个元素由提供的Supplier生成,适用于生成恒定流、随机元素流
        Stream<Integer> generate = Stream.generate(() -> (int) (Math.random() * 10));// 不断产生 0-9 的随机数
        System.out.println("\n\ngenerate() 返回一个有规律的无限顺序流,限制输出前10个: ");
        generate.limit(10).forEach(item -> System.out.print(item + " "));
    }

运行结果:

of() 将参数数组作为数据源,返回一个顺序流: 
0 1 5 2 4 8 9 

iterate() 返回一个有规律的无限顺序流,限制输出前10: 
0 2 4 6 8 10 12 14 16 18 

generate() 返回一个有规律的无限顺序流,限制输出前10: 
2 6 9 2 7 1 4 6 0 0 

2 终止操作

因为 Stream 有惰性求值的特点,中间操作,并不会立即执行,只有触发终止操作时,才会进行实际的运算。

Stream 操作可以有 0个或者多个中间操作,但是 有且只有一个终止操作

所以下面为了方便演示,我们讲解终止操作的使用,再讲解中间操作的使用。

2.1 allMatch

allMatch操作,如果流中所有元素都匹配 predicate 或者是空流,返回true,否则返回false

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * allMatch操作,如果流中所有元素都匹配 predicate 或者是空流,返回true,否则返回false
     */
    @Test
    public void testStreamAllMatch() {
        Stream<Person> emptyStream = Stream.empty();
        Stream<Person> stream = persons.stream();
        // 流中所有Person元素都大于20岁
        boolean allMathchGt20 = stream.allMatch(item -> item.getAge() > 20);
        System.out.println("流中所有Person元素都大于20岁: " + allMathchGt20);
        // 空流中所有Person元素都大于20岁
        boolean allMathchGt20InEmptyStream = emptyStream.allMatch(item -> item.getAge() > 20);
        System.out.println("空流中所有Person元素都大于20岁: " + allMathchGt20InEmptyStream);
    }
}    

运行结果:

流中所有Person元素都大于20: false
空流中所有Person元素都大于20: true

2.2 noneMatch

noneMatch 操作,如果流中没有元素匹配 predicate 或者是空流,返回true,否则返回false

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * noneMatch 操作,如果流中没有元素匹配 predicate 或者是空流,返回true,否则返回false
     */
    @Test
    public void testStreamNoneMatch() {
        Stream<Person> emptyStream = Stream.empty();
        Stream<Person> stream = persons.stream();
        // 流中没有Person元素大于30岁
        boolean noneMathchGt30 = stream.noneMatch(item -> item.getAge() > 30);
        System.out.println("流中没有Person元素大于30岁: " + noneMathchGt30);
        // 空流中所有Person元素都大于30岁
        boolean noneMathchGt30InEmptyStream = emptyStream.noneMatch(item -> item.getAge() > 30);
        System.out.println("空流中没有Person元素大于30岁: " + noneMathchGt30InEmptyStream);
    }
}    

运行结果:

流中没有Person元素大于30: true
空流中没有Person元素大于30: true

2.3 anyMatch

anyMatch 操作,如果流中任意一个元素匹配 predicate返回true,否则返回false,空流返回false

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * anyMatch 操作,如果流中任意一个元素匹配 predicate返回true,否则返回false,空流返回false
     */
    @Test
    public void testStreamAnyMatch() {
        Stream<Person> emptyStream = Stream.empty();
        Stream<Person> stream = persons.stream();
        // 流中任意一个Person元素都小于18岁
        boolean anyMathchGt18 = stream.anyMatch(item -> item.getAge() > 18);
        System.out.println("流中任意一个Person元素都小于18岁: " + anyMathchGt18);
        // 空流中任意一个Person元素都小于18岁
        boolean anyMathchGt18InEmptyStream = emptyStream.anyMatch(item -> item.getAge() > 18);
        System.out.println("空流中任意一个Person元素都小于18岁: " + anyMathchGt18InEmptyStream);
    }
}    

运行结果:

流中任意一个Person元素都小于18: true
空流中任意一个Person元素都小于18: false

2.4 collect

collect操作:接收一个Collector实例,将流中元素收集成另外一个数据结构

2.4.1 介绍 Collector 和 Collectors

Collector 作为 Stream 的collect操作的参数类型,能够把流中的元素序列收集到另一个数据结构中。
Collectors是工具类,提供了很多静态方法用于生成Collector实例

Collectors常用API

静态方法 描述
Collector<T, ?, List> toList() 将输入的元素,收集到一个List容器中
Collector<T, ?, Set> toSet() 将输入的元素,收集到一个Set容器中
Collector<T, ?, Map<K,U>> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
第一个参数keyMapper:获取map的key的Function
第二个参数valueMapper:获取map的value的Function
将输入的元素,收集到一个Map<K,U>>容器中
Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
第一个参数keyMapper:获取map的key的Function
第二个参数valueMapper:获取map的value的Function
将输入的元素,收集到一个ConcurrentMap<K,U>容器

2.4.2 collect操作测试用例

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * collect操作:接收一个Collector实例,将流中元素收集成另外一个数据结构
     */
    @Test
    public void testStreamCollect() {
        Stream<Person> stream2List = persons.stream();
        // 将流中元素收集成 List
        List<Person> list = stream2List.collect(Collectors.toList());
        System.out.println("将流中元素收集成 List :\n" + list);

        Stream<Person> stream2Map = persons.stream();
        // 将流中元素收集成 map,key: name,value: person
        // Function,通过输入的Person,获取name作为map的key
        Function<Person, String> nameFunc = item -> item.getName();
        // Function,通过输入的Person,获取 person 作为map的value
        Function<Person, Person> personFunc = item -> item;
        Map<String, Person> map = stream2Map.collect(Collectors.toMap(nameFunc, personFunc));
        System.out.println("\n将流中元素收集成 map,key: name,value: person :\n" + map);
    }
}    

运行结果:

将流中元素收集成 List :
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

将流中元素收集成 map,key: name,value: person :
{孙七=Person{name='孙七', age=16}, 李四=Person{name='李四', age=29}, 张三=Person{name='张三', age=24}, 刘一=Person{name='刘一', age=25}, 陈二=Person{name='陈二', age=12}, 王五=Person{name='王五', age=22}, 周八=Person{name='周八', age=18}, 赵六=Person{name='赵六', age=15}}

2.5 count

count操作:返回到达终止操作时,流中元素总数

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * count操作:返回到达终止操作时,流中元素总数
     */
    @Test
    public void testStreamCount() {
        Stream<Person> stream = persons.stream();
        // 流中元素总数
        long count = stream.count();
        System.out.println("流中元素总数: " + count);
    }
}    

运行结果:

流中元素总数: 8

2.6 findAny

findAny 操作:返回任意一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null。

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * findAny 操作:返回任意一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
     */
    @Test
    public void testStreamFindAny() {
        Stream<Person> stream = persons.stream();
        // 非空流:返回任意一个元素
        Optional<Person> any = stream.findAny();
        System.out.println("非空流:返回任意一个元素: " + any);

        Stream<Person> emptyStream = Stream.empty();
        // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
        Optional<Person> findAnyEmpty = emptyStream.findAny();
        System.out.println("空流:取任意一个元素时返回空的Optional: " + findAnyEmpty);
    }
}    

运行结果:

非空流:返回任意一个元素: Optional[Person{name='刘一', age=25}]
空流:取任意一个元素时返回空的Optional: Optional.empty

2.7 findFirst

findFirst操作:返回流中第一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null。

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * findFirst操作:返回流中第一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
     */
    @Test
    public void testStreamFindFirst() {
        Stream<Person> stream = persons.stream();
        // 非空流:返回流中第一个元素
        Optional<Person> first = stream.findFirst();
        System.out.println("非空流:返回流中第一个元素: " + first);

        Stream<Person> emptyStream = Stream.empty();
        // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
        Optional<Person> findFirstEmpty = emptyStream.findFirst();
        System.out.println("空流:取第一个第一个元素时返回空的Optional: " + findFirstEmpty);
    }
}    

运行结果:

非空流:返回流中第一个元素: Optional[Person{name='刘一', age=25}]
空流:取第一个第一个元素时返回空的Optional: Optional.empty

2.8 forEach

forEach操作:对此流的每个元素执行 Consumer 操作。

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * forEach操作:对此流的每个元素执行 Consumer 操作
     */
    @Test
    public void testStreamForEach() {
        Stream<Person> stream = persons.stream();
        //forEach操作:对此流的每个元素执行 Consumer 操作
        // 打印流中的每一个元素
        System.out.println("打印流中的每一个元素:");
        stream.forEach(item -> System.out.println(item));

        Stream<Person> stream2 = persons.stream();
        // 把流中的每一个元素收集进list
        List<Person> list = new ArrayList<>();
        stream2.forEach(list::add);
        System.out.println("\n把流中的每一个元素收集进list:");
        System.out.println(list);
    }
}    

运行结果:

打印流中的每一个元素:
Person{name='刘一', age=25}
Person{name='陈二', age=12}
Person{name='张三', age=24}
Person{name='李四', age=29}
Person{name='王五', age=22}
Person{name='赵六', age=15}
Person{name='孙七', age=16}
Person{name='周八', age=18}

把流中的每一个元素收集进list:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

2.9 max

max 操作:根据给定的Comparator,返回流中最大的元素,用Optional描述,
如果是空流,返回空的Optional(Optional.empty),Optional的value值为null

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * max 操作:根据给定的Comparator,返回流中最大的元素,用Optional描述,
     * 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
     */
    @Test
    public void testStreamMax() {
        Stream<Person> stream = persons.stream();

        // 返回流中年龄最大的元素
        // Comparator.comparingInt(person -> person.getAge()) 等同于 (p1, p2) -> p1.getAge() - p2.getAge()
        // stream.max(Comparator.comparingInt(person -> person.getAge()));
        // person的age属性比较器
        Comparator<Person> ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();
        Optional<Person> max = stream.max(ageComparator);
        System.out.println("返回流中最大的元素:" + max);

        Stream<Person> emptyStream = Stream.empty();
        // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
        Optional<Person> maxInEmpty = emptyStream.max(ageComparator);
        System.out.println("空流:取最大元素时返回空的Optional: " + maxInEmpty);
    }
}    

运行结果:

返回流中最大的元素:Optional[Person{name='李四', age=29}]
空流:取最大元素时返回空的Optional: Optional.empty

2.10 min

min 操作:根据给定的Comparator,返回流中最小的元素,用Optional描述
如果是空流,返回空的Optional(Optional.empty),Optional的value值为null

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * min操作:根据给定的Comparator,返回流中最小的元素,用Optional描述,
     * 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
     */
    @Test
    public void testStreamMin() {
        Stream<Person> stream = persons.stream();

        // 返回流中年龄最小的元素
        // Comparator.comparingInt(person -> person.getAge()) 等同于 (p1, p2) -> p1.getAge() - p2.getAge()
        // stream.min(Comparator.comparingInt(person -> person.getAge()));
        // person的age属性比较器
        Comparator<Person> ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();
        Optional<Person> min = stream.min(ageComparator);
        System.out.println("返回流中最小的元素:" + min);

        Stream<Person> emptyStream = Stream.empty();
        // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
        Optional<Person> minInEmpty = emptyStream.min(ageComparator);
        System.out.println("空流:去最小元素时返回空的Optional: " + minInEmpty);
    }
}    

运行结果:

返回流中最小的元素:Optional[Person{name='陈二', age=12}]
空流:去最小元素时返回空的Optional: Optional.empty

2.11 Optional reduce(BinaryOperator accumulator)

Optional reduce(BinaryOperator accumulator):
第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;
第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;
依次类推。最终得到用Optional描述的T类型的结果。

    /**
     * Optional reduce(BinaryOperator accumulator):
     * 第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;
     * 第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;
     * 依次类推。最终得到用Optional描述的T类型的结果。
     */
    @Test
    public void testStreamReduce1() {
        // 计算 nums 数组中所有元素的和

        int[] nums = new int[]{1, 5, 2, 4, 8, 9};
        // Arrays.stream(T[] array) 将指定的数组作为数据源,返回一个顺序流
        IntStream stream = Arrays.stream(nums);
        // (n1, n2) -> n1 + n2 二元运算 lambda 表达式,用于返回两个数值 n1 + n2 的结果
        OptionalInt reduce = stream.reduce((n1, n2) -> n1 + n2);
        System.out.println("计算 nums 数组中素有元素的和: " + reduce.getAsInt());
    }

运行结果:

计算 nums 数组中素有元素的和: 29

2.12 T reduce(T identity, BinaryOperator accumulator)

T reduce(T identity, BinaryOperator accumulator):
第一次执行时,accumulator函数的第一个参数identity,第二个参数为流中元素的第一个元素;
第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第二个元素;
依次类推。最终得到T类型的结果。

    /**
     * T reduce(T identity, BinaryOperator accumulator):
     * 第一次执行时,accumulator函数的第一个参数identity,第二个参数为流中元素的第一个元素;
     * 第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第二个元素;
     * 依次类推。最终得到T类型的结果。
     */
    @Test
    public void testStreamReduce2() {
        // strs 数组中所有元素用空格连接,并加上 "黑桃" 字符串作为前缀

        String[] strs = new String[]{"hello", "Stream", "终止操作", "reduce"};
        // Arrays.stream(strs) 将指定的数组作为数据源,返回一个顺序流
        Stream<String> stream = Arrays.stream(strs);
        // (n1, n2) -> n1 + n2 二元运算 lambda 表达式,把 str1 和 str2 两个字符串用 " " 空格连接
        BinaryOperator<String> binaryOperator = (str1, str2) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(str1).append(" ").append(str2);
            return sb.toString();
        };
        String reduce = stream.reduce("黑桃", binaryOperator);
        System.out.println("strs 数组中所有元素用空格连接,并加上 \"黑桃\" 字符串作为前缀:\n " + reduce);
    }

运行结果:

strs 数组中所有元素用空格连接,并加上 "黑桃" 字符串作为前缀:
 黑桃 hello Stream 终止操作 reduce

2.13 toArray

Object[] toArray() 操作:返回包含此流元素的数组.

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * Object[] toArray() 操作:返回包含此流元素的数组
     */
    @Test
    public void testToArray() {
        Stream<Person> stream = persons.stream();
        // 返回包含此流元素的数组
        Object[] objects = stream.toArray();
        System.out.println(Arrays.asList(objects));
    }
}    

运行结果:

[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

3 中间操作

中间操作可以像数据库操作一样,让我们可以对数据源进行一些聚合操作,比如 distinct、limit…

**再次提示:**Stream 操作可以有 0个或者多个中间操作,但是 有且只有一个终止操作

这里我们先演示每个中间操作的单独使用,最后再演示多个中间操作串联的测试用例。

3.1 distinct

distinct 操作:返回由不同元素组成的流,根据元素的 equals() 方法去重

public 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 void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return getAge() == person.getAge() &&
                Objects.equals(getName(), person.getName());
    }

    @Override
    public int hashCode() {

        return Objects.hash(getName(), getAge());
    }

}

    /**
     * distinct 操作:返回由不同元素组成的流,根据元素的equals()方法去重
     */
    @Test
    public void testDistinct() {
        Person[] personArray = new Person[]{
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("张三", 24),
                new Person("王五", 22),
                new Person("赵六", 15)
        };
        Stream<Person> stream = Arrays.stream(personArray);
        System.out.println("根据元素的equals()方法去重:");
        stream.distinct().forEach(item -> System.out.println(item));
    }

运行结果:

根据元素的equals()方法去重:
Person{name='张三', age=24}
Person{name='李四', age=29}
Person{name='王五', age=22}
Person{name='赵六', age=15}

3.2 filter

filter 操作:过滤,排除掉没有匹配 predicate 函数式接口的元素

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * filter 操作:过滤,排除掉没有匹配 predicate 函数式接口的元素
     */
    @Test
    public void testFilter() {
        //过滤,返回年龄大于25岁的Person
        System.out.println("返回年龄大于25岁的Person:");
        // Predicate 函数式接口的 lambda表达式,判断传入的 person 的年龄是否大于25岁
        Predicate<Person> ageGt25Predicate = person -> person.getAge() > 25;
        persons.stream()
                .filter(ageGt25Predicate)
                .forEach(item -> System.out.println(item));
    }
}    

运行结果:

返回年龄大于25岁的Person:
Person{name='李四', age=29}

3.3 limit

limit 操作:限制流的元素数量,截断流,使其元素不超过给定数量

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * limit 操作:限制流的元素数量,截断流,使其元素不超过给定数量
     */
    @Test
    public void testlimit() {
        System.out.println("persons 列表:\n" + persons);

        // 返回 persons 列表中前3个元素
        List<Person> collect = persons.stream()
                .limit(3)//限制流中的最大元素数量为3
                .collect(Collectors.toList());//将流中元素收集到list中
        System.out.println("\n返回 persons 列表中前3个元素:\n" + collect);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

返回 persons 列表中前3个元素:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}]

3.4 skip

skip 操作:跳过元素,返回一个扔掉了前 n 个元素的流。如果此流包含的元素少于n,则返回空流

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * skip 操作:跳过元素,返回一个扔掉了前 n 个元素的流。如果此流包含的元素少于n,则返回空流
     */
    @Test
    public void testSkip() {
        System.out.println("persons 列表:\n" + persons);

        // 跳过 persons 列表中前3个元素
        List<Person> collect = persons.stream()
                .skip(3)//跳过流中的前3个元素
                .collect(Collectors.toList());//将流中元素收集到list中
        System.out.println("\n跳过 persons 列表中前3个元素:\n" + collect);

        // 如果此流包含的元素少于n,则返回空流
        List<Person> empty = persons.stream()
                //跳过流中的前10个元素,流包含的元素少于10,返回空流
                .skip(10)
                .collect(Collectors.toList());//将流中元素收集到list中
        System.out.println("\n跳过 persons 列表中前10个元素:\n" + empty);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

跳过 persons 列表中前3个元素:
[Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

跳过 persons 列表中前10个元素:
[]


3.5 sorted()

sorted 操作:自然排序后返回一个新的流,如果元素不是Comparable类型,则抛出ClassCastException


    /**
     * sorted 操作:自然排序后返回一个新的流,如果元素不是Comparable类型,则抛出ClassCastException
     */
    @Test
    public void testSorted() {
        int[] nums = new int[]{1, 5, 2, 4, 8, 9};
        System.out.println("对数组进行自然排序:");
        Arrays.stream(nums).sorted().forEach(System.out::println);
    }

运行结果:

对数组进行自然排序:
1
2
4
5
8
9

3.6 Stream sorted(Comparator<? super T> comparator)

Stream sorted(Comparator<? super T> comparator) 操作:根据给定 Comparator 排序后,返回一个新流。

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * Stream sorted(Comparator<? super T> comparator) 操作:根据给定 Comparator 排序后,返回一个新流
     */
    @Test
    public void testSorted2() {
        System.out.println("persons 列表:\n" + persons);
        // person的age属性比较器
        Comparator<Person> ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();

        System.out.println("按照 age 升序排序:");
        persons.stream()
                // 根据给定 Comparator 排序
                .sorted(ageComparator)
                .forEach(System.out::println);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
按照 age 升序排序:
Person{name='陈二', age=12}
Person{name='赵六', age=15}
Person{name='孙七', age=16}
Person{name='周八', age=18}
Person{name='王五', age=22}
Person{name='张三', age=24}
Person{name='刘一', age=25}
Person{name='李四', age=29}

3.7 map

map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
     */
    @Test
    public void testMap() {
        System.out.println("persons 列表:\n" + persons);

        //获取 persons 中每个元素的 name 属性存放到  List<String> 中
        List<String> list = persons.stream()
                //map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
                // Function函数式接口,获取入参person中的 name 属性
                // 将 Person 类型元素映射成String类型的新元素
                .map(person -> person.getName())
                // 将流中元素收集成 List
                .collect(Collectors.toList());
        System.out.println("获取 persons 中每个元素的 name 属性存放到  List<String> 中:\n" + list);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
获取 persons 中每个元素的 name 属性存放到  List<String> 中:
[刘一, 陈二, 张三, 李四, 王五, 赵六, 孙七, 周八]

3.8 mapToInt、mapToLong、mapToDouble

mapToInt 操作:通过入参ToIntFunction函数式接口将流中的每个T类型元素映射成一个Integer类型的新元素
mapToLong 操作:通过入参ToLongFunction函数式接口将流中的每个T类型元素映射成一个Long类型的新元素
mapToDouble 操作:通过入参ToDoubleFunction函数式接口将流中的每个T类型元素映射成一个Double类型的新元素

mapToInt简单使用

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * mapToInt 操作:通过入参ToIntFunction函数式接口将流中的每个T类型元素映射成一个Integer类型的新元素
     */
    @Test
    public void testMapToInt() {
        System.out.println("persons 列表:\n" + persons);

        //获取 persons 中每个元素的 age 属性存放到  List<Integer> 中
        List<Integer> list = new ArrayList<>();
        persons.stream()
                //map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
                // Function函数式接口,获取入参person中的 age 属性
                // 将 Person 类型元素映射成 int 类型的新元素
                .mapToInt(person -> person.getAge())
                // 将流中元素收集成 List
                .forEach(list::add);
        System.out.println("获取 persons 中每个元素的 age 属性存放到  List<Integer> 中:\n" + list);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
获取 persons 中每个元素的 age 属性存放到  List<Integer> 中:
[25, 12, 24, 29, 22, 15, 16, 18]

3.9 peek

peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作,如给元素属性赋值,每个元素引用没变

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作,如给元素属性赋值,每个元素引用没变
     */
    @Test
    public void testPeek() {
        System.out.println("persons 列表:\n" + persons);

        System.out.println("\n给 persons 中每个元素的 name 属性添加\"_peek\"后缀:");
        //给 persons 中每个元素的 name 属性添加"_peek"后缀
        persons.stream()
                //peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作
                // Consumer函数式接口,将入参person中的 name 属性添加"_peek"后缀
                // 将 Person 类型元素映射成String类型的新元素
                .peek(person -> {
                    String name = person.getName();
                    person.setName(name + "_peek");
                })
                .forEach(System.out::println);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

给 persons 中每个元素的 name 属性添加"_peek"后缀:
Person{name='刘一_peek', age=25}
Person{name='陈二_peek', age=12}
Person{name='张三_peek', age=24}
Person{name='李四_peek', age=29}
Person{name='王五_peek', age=22}
Person{name='赵六_peek', age=15}
Person{name='孙七_peek', age=16}
Person{name='周八_peek', age=18}

3.10 flatMap

flatMap 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个流,然后把转换后的所有流连接成一个流

    /**
     * flatMap 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个流,然后把转换后的所有流连接成一个流
     */
    @Test
    public void testFlatMap() {
        Person[] array1 = new Person[]{
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
        };
        Person[] array2 = new Person[]{
                new Person("刘一", 25),
                new Person("陈二", 12),
        };
        Person[] array3 = new Person[]{
                new Person("孙七", 16),
                new Person("周八", 18)
        };
        // 将 array1、array2、array3 3个person数组的所有Person元素收集存放到  List<Person> 中
        List<Person> list = Stream.of(array1, array2, array3)
                //flatMap 操作:通过入参Function函数式接口将流中的每个元素(Person[])都换成单独一个流,
                // 然后把转换后的所有流连接成一个流
                .flatMap(array -> Arrays.stream(array))
                // 将流中元素收集成 List
                .collect(Collectors.toList());
        System.out.println("将 array1、array2、array3 3个person数组的所有Person元素收集存放到  List<Person> 中:\n" + list);
    }   

运行结果:

将 array1、array2、array3 3个person数组的所有Person元素收集存放到  List<Person> 中:
[Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

3.11 flatMapToInt、flatMapToLong、flatMapToDouble

flatMapToIn 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个IntStream(流中元素的类型是Integer),然后把转换后的所有IntStream连接成一个IntStream
flatMapToLong 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个LongStream(流中元素的类型是Long),然后把转换后的所有LongStream连接成一个LongStream
flatMapToDouble 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个DoubleStream(流中元素的类型是Double),然后把转换后的所有DoubleStream连接成一个DoubleStream

flatMapToInt 简单使用

    /**
     * flatMapToInt 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个IntStream(流中元素的类型是Integer),
     * 然后把转换后的所有IntStream连接成一个IntStream
     */
    @Test
    public void testFlatMapToInt() {
        Person[] array1 = new Person[]{
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
        };
        Person[] array2 = new Person[]{
                new Person("刘一", 25),
                new Person("陈二", 12),
        };
        Person[] array3 = new Person[]{
                new Person("孙七", 16),
                new Person("周八", 18)
        };
        // 将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List<Integer> 中:
        List<Integer> list = new ArrayList<>();
        Stream.of(array1, array2, array3)
                //flatMap 操作:通过入参Function函数式接口将流中的每个元素(Person[])都换成单独一个流,
                // 然后把转换后的所有流连接成一个流
                .flatMapToInt(array -> {
                    int[] ages = new int[array.length];
                    for (int i = 0; i < array.length; i++) {
                        ages[i] = array[i].getAge();
                    }
                    return IntStream.of(ages);
                })
                // 将流中元素收集成 List
                .forEach(item -> list.add(item));
        System.out.println("将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List<Integer> 中:\n" + list);
    }

运行结果:

将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List<Integer> 中:
[24, 29, 22, 25, 12, 16, 18]

3.12 多个中间操作连接

public class StreamAPITest {
    List<Person> persons = null;

    @Before
    public void before() {
        persons = Arrays.asList(
                new Person("刘一", 25),
                new Person("陈二", 12),
                new Person("张三", 24),
                new Person("李四", 29),
                new Person("王五", 22),
                new Person("赵六", 15),
                new Person("孙七", 16),
                new Person("周八", 18)
        );
    }
    
    /**
     * 测试多个中间操作:Stream操作中支持 0个或者多个 中间操作
     */
    @Test
    public void testMultiOpr() {
        System.out.println("persons 列表:\n" + persons);

        //将 persons 中 age 大于18 的前两个person的name 收集到  List<String> 中
        List<String> collect = persons.stream()
                //过滤出年龄大于18的元素
                .filter(person -> person.getAge() > 18)
                //限制返回前两个元素
                .limit(2)
                // 映射:返回person的name属性
                .map(person -> person.getName())
                // 将流中的元素收集到 list 中
                .collect(Collectors.toList());
        System.out.println("\n将 persons 中 age 大于18 的前两个person的name 收集到  List<String> 中:");
        System.out.println(collect);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

将 persons 中 age 大于18 的前两个person的name 收集到  List<String>:
[刘一, 张三]

Stream 提供了大量的对集合等数据源的增强操作,上面给大家演示的是工作中比较常用的创建中间操作终止操作的API使用。
Stream+Lambda表达式 让我们对集合等数据源的操作更加简洁清晰,便于代码的维护。希望大家学以致用,熟能生巧。

0人推荐
随时随地看视频
慕课网APP