目录

一:集合处理数据的弊端

二:Stream流式思想概述

小结 :

三:获取Stream流的两种方式

方式1 : 根据Collection获取流

方式2 : Stream中的静态方法of获取流

小结

四:Stream常用方法和注意事项

Stream常用方法

Stream注意事项(重要)

Stream流的forEach方法

Stream流的count方法

Stream流的fifilter方法

Stream流的limit方法

Stream流的skip方法

Stream流的map方法

Stream流的sorted方法

Stream流的distinct方法

Stream流的match方法

Stream流的fifind方法

Stream流的max和min方法

Stream流的reduce方法

Stream流的map和reduce组合使用

Stream流的mapToInt

Stream流的concat方法

四:Stream综合案例

传统方式

Stream方式


一:集合处理数据的弊端

                当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验集合操作数据的弊端,需求如下:

一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据

代码如下:

public static void main(String[] args) {
// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
// 1.拿到所有姓张的
ArrayList<String> zhangList = new ArrayList<>(); // {"张无忌", "张强", "张三丰"}
for (String name : list) {
if (name.startsWith("张")) {
zhangList.add(name);
}
}
// 2.拿到名字长度为3个字的
ArrayList<String> threeList = new ArrayList<>(); // {"张无忌", "张三丰"}
for (String name : zhangList) {
if (name.length() == 3) {
threeList.add(name);
}
}
// 3.打印这些数据
for (String name : threeList) {
System.out.println(name);
}
}

循环遍历的弊端

这段代码中含有三个循环,每一个作用不同:

1. 首先筛选所有姓张的人;

2. 然后筛选名字有三个字的人;

3. 最后进行对结果进行打印输出。

               每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么? 不是。 循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使用另一个循环从头开始。

那 Stream 能给我们带来怎样更加优雅的写法呢?

下面来看一下借助 Java 8 的 Stream API ,修改后的代码:

public class Demo03StreamFilter {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.stream()
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
}
}

              直接阅读代码的字面意思即可完美展示无关逻辑方式的语义: 获取流、过滤姓张、过滤长度为 3 、逐一打印 。我们真正要做的事情内容被更好地体现在代码中。

二:Stream流式思想概述

                 注意: Stream 和 IO 流 (InputStream/OutputStream) 没有任何关系,请暂时忘记对传统 IO 流的固有印象!

                    Stream 流式思想类似于工厂车间的 “ 生产流水线 ” , Stream 流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream 可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

java stream流操作 限制最大长度 java stream流操作缺点_java

 

java stream流操作 限制最大长度 java stream流操作缺点_java_02

 

java stream流操作 限制最大长度 java stream流操作缺点_数据_03

Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约。

小结 :

             首先我们了解了集合操作数据的弊端 , 每次都需要循环遍历 , 还要创建新集合 , 很麻烦 ,Stream是流式思想 , 相当于工厂的流水线 , 对集合中的数据进行加工处理

三:获取Stream流的两种方式

  java.util.stream.Stream<T> 是 JDK 8 新加入的流接口。

  获取一个流非常简单,有以下几种常用的方式:

  • 所有的 Collection 集合都可以通过 stream 默认方法获取流;
  • Stream 接口的静态方法 of 可以获取数组对应的流。

方式1 : 根据Collection获取流

           首先, java.util.Collection 接口中加入了 default 方法 stream 用来获取流,所以其所有实现类均可获取流。

public interface Collection {
default Stream<E> stream()
}
import java.util.*;
import java.util.stream.Stream;
public class Demo04GetStream {
public static void main(String[] args) {
// 集合获取流
// Collection接口中的方法: default Stream<E> stream() 获取流
List<String> list = new ArrayList<>();
// ...
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
// ...
Stream<String> stream2 = set.stream();
Vector<String> vector = new Vector<>();
// ...
Stream<String> stream3 = vector.stream();
}
}

        java.util.Map 接口不是 Collection 的子接口,所以获取对应的流需要分 key 、 value 或 entry 等情况:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class Demo05GetStream {
public static void main(String[] args) {
// Map获取流
Map<String, String> map = new HashMap<>();
// ...
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}
}

方式2 : Stream中的静态方法of获取流

                  由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单:

import java.util.stream.Stream;
public class Demo06GetStream {
public static void main(String[] args) {
// Stream中的静态方法: static Stream of(T... values)
Stream<String> stream6 = Stream.of("aa", "bb", "cc");
String[] arr = {"aa", "bb", "cc"};
Stream<String> stream7 = Stream.of(arr);
Integer[] arr2 = {11, 22, 33};
Stream<Integer> stream8 = Stream.of(arr2);
// 注意:基本数据类型的数组不行,它会将整个数组看做一个元素进行操作
int[] arr3 = {11, 22, 33};
Stream<int[]> stream9 = Stream.of(arr3);
}
}

        备注: of 方法的参数其实是一个可变参数,所以支持数组

小结

       学习了两种获取流的方式 :

1. 通过 Collection 接口中的默认方法 Stream stream()

2. 通过 Stream 接口中的静态 of 方法

四:Stream常用方法和注意事项

Stream常用方法

  Stream 流模型的操作很丰富,这里介绍一些常用的 API 。这些方法可以被分成两种:

方法名

方法作用

返回值类

方法种类

 count

统计个数

long

终结

 forEach

逐一处理

void

终结

 fifilter

过滤

Stream

函数拼接

 limit

取用前几个

Stream

函数拼接

skip

跳过前几个

Stream

函数拼接

map

映射

Stream

函数拼接

concat

组合

Stream

函数拼接

  • 终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和 forEach 方法。
  • 非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结方法。)

Stream注意事项(重要)

1. Stream 只能操作一次

2. Stream 方法返回的是新的流

3. Stream 不调用终结方法,中间的操作不会执行

public class Demo03StreamNotice {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("aa", "bb", "cc");
        // 1. Stream只能操作一次
        // long count = stream.count();
        // long count2 = stream.count();

        // 2. Stream方法返回的是新的流
        // Stream<String> limit = stream.limit(1);
        // System.out.println("stream" + stream);
        // System.out.println("limit" + limit);

        // 3. Stream不调用终结方法,中间的操作不会执行
        stream.filter((s) -> {
            System.out.println(s);
            return true;
        }).count();
    }
}

Stream流的forEach方法

    forEach 用来遍历流中的数据

void forEach(Consumer<? super T> action);

     该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。例如:

@Test
    public void testForEach() {
        List<String> one = new ArrayList<>();
        Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");

        /*// 得到流
        // 调用流中的方法
        one.stream().forEach((String str) -> {
            System.out.println(str);
        });

        // Lambda可以省略
        one.stream().forEach(str -> System.out.println(str));*/

        // Lambda可以转成方法引用
        one.stream().forEach(System.out::println);

    }

Stream流的count方法

     Stream 流提供 count 方法来统计其中的元素个数:

long count();

     该方法返回一个 long 值代表元素个数。基本使用:

@Test
    public void testCount() {
        List<String> one = new ArrayList<>();
        Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");

        long count = one.stream().count();
        System.out.println(count);
    }

Stream流的fifilter方法

     fifilter 用于过滤数据,返回符合过滤条件的数据

java stream流操作 限制最大长度 java stream流操作缺点_System_04

     可以通过 filter 方法将一个流转换成另一个子集流。方法声明:

Stream<T> filter(Predicate<? super T> predicate);

             该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或方法引用)作为筛选条件。

             Stream 流中的 filter 方法基本使用的代码如:

@Test
    public void testFilter() {
        List<String> one = new ArrayList<>();
        Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");

        // 得到名字长度为3个字的人(过滤)
        // filter(Predicate<? super T> predicate)
        /*one.stream().filter((String s) -> {
            return s.length() == 3;
        }).forEach(System.out::println);*/

        // one.stream().filter(s -> s.length() == 3).forEach(System.out::println);
    }

Stream流的limit方法

java stream流操作 限制最大长度 java stream流操作缺点_java_05

limit 方法可以对流进行截取,只取用前n个。方法签名:

Stream<T> limit(long maxSize);

  参数是一个 long 型,如果集合当前长度大于参数则进行截取。否则不进行操作。基本使用:

@Test
    public void testLimit() {
        List<String> one = new ArrayList<>();
        Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");

        // 获取前3个数据
        one.stream()
                .limit(3)
                .forEach(System.out::println);
    }

Stream流的skip方法

java stream流操作 限制最大长度 java stream流操作缺点_JDK_06

     如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:

Stream<T> skip(long n);

      如果流的当前长度大于 n ,则跳过前 n 个;否则将会得到一个长度为 0 的空流。基本使用:

@Test
    public void testSkip() {
        List<String> one = new ArrayList<>();
        Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");

        // 跳过前两个数据
        one.stream()
                .skip(2)
                .forEach(System.out::println);
    }

Stream流的map方法

java stream流操作 限制最大长度 java stream流操作缺点_数据_07

 

如果需要将流中的元素映射到另一个流中,可以使用 map 方法。方法签名:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

           该接口需要一个 Function 函数式接口参数,可以将当前流中的 T 类型数据转换为另一种 R 类型的流。

Stream 流中的 map 方法基本使用的代码如:

@Test
    public void testMap() {
        Stream<String> original = Stream.of("11", "22", "33");

        // Map可以将一种类型的流转换成另一种类型的流
        // 将Stream流中的字符串转成Integer
        /*Stream<Integer> stream = original.map((String s) -> {
            return Integer.parseInt(s);
        });*/
        // original.map(s -> Integer.parseInt(s)).forEach(System.out::println);

        original.map(Integer::parseInt).forEach(System.out::println);
    }

             这段代码中, map 方法的参数通过方法引用,将字符串类型转换成为了 int 类型(并自动装箱为 Integer 类对象)。

Stream流的sorted方法

如果需要将数据排序,可以使用 sorted 方法。方法签名:

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

基本使用

Stream 流中的 sorted 方法基本使用的代码如:

@Test
    public void testSorted() {
        // sorted(): 根据元素的自然顺序排序
        // sorted(Comparator<? super T> comparator): 根据比较器指定的规则排序
        Stream<Integer> stream = Stream.of(33, 22, 11, 55);

        // stream.sorted().forEach(System.out::println);
        /*stream.sorted((Integer i1, Integer i2) -> {
            return i2 - i1;
        }).forEach(System.out::println);*/

        stream.sorted((i1, i2) -> i2 - i1).forEach(System.out::println);
    }

      这段代码中, sorted 方法根据元素的自然顺序排序,也可以指定比较器排序。

Stream流的distinct方法

java stream流操作 限制最大长度 java stream流操作缺点_java_08

如果需要去除重复数据,可以使用 distinct 方法。方法签名:

Stream<T> distinct();

基本使用

Stream 流中的 distinct 方法基本使用的代码如:

@Test
    public void testDistinct() {
        Stream<Integer> stream = Stream.of(22, 33, 22, 11, 33);

        stream.distinct().forEach(System.out::println);

        Stream<String> stream1 = Stream.of("aa", "bb", "aa", "bb", "cc");
        stream1.distinct().forEach(System.out::println);

    }

如果是自定义类型如何是否也能去除重复的数据呢?

@Test
public void testDistinct2() {
Stream.of(
new Person("刘德华", 58),
new Person("张学友", 56),
new Person("张学友", 56),
new Person("黎明", 52))
.distinct()
.forEach(System.out::println);
}
public class Person {
private String name;
private int age;
// 省略其他
}

自定义类型是根据对象的 hashCode 和 equals 来去除重复元素的。

// distinct对自定义对象去除重复
    @Test
    public void testDistinct2() {
        Stream<Person> stream = Stream.of(
                new Person("貂蝉", 18),
                new Person("杨玉环", 20),
                new Person("杨玉环", 20),
                new Person("西施", 16),
                new Person("西施", 16),
                new Person("王昭君", 25)
        );

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

重写对象的hashCode和equals即可

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

Stream流的match方法

如果需要判断数据是否匹配指定的条件,可以使用 Match 相关方法。方法签名:

boolean allMatch(Predicate<? super T> predicate);
boolean anyMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);

基本使用

Stream 流中的 Match 相关方法基本使用的代码如:

@Test
    public void testMatch() {
        Stream<Integer> stream = Stream.of(5, 3, 6, 1);

        // boolean b = stream.allMatch(i -> i > 0); // allMatch: 匹配所有元素,所有元素都需要满足条件
        // boolean b = stream.anyMatch(i -> i > 5); // anyMatch: 匹配某个元素,只要有其中一个元素满足条件即可
        boolean b = stream.noneMatch(i -> i < 0); // noneMatch: 匹配所有元素,所有元素都不满足条件
        System.out.println(b);
    }

Stream流的fifind方法

java stream流操作 限制最大长度 java stream流操作缺点_开发语言_09

如果需要找到某些数据,可以使用 find 相关方法。方法签名:

Optional<T> findFirst();
Optional<T> findAny();

基本使用

Stream 流中的 find 相关方法基本使用的代码如:

@Test
    public void testFind() {
        Stream<Integer> stream = Stream.of(33, 11, 22, 5);
        Optional<Integer> first = stream.findFirst();
        // Optional<Integer> first = stream.findAny();
        System.out.println(first.get());
    }

Stream流的max和min方法

java stream流操作 限制最大长度 java stream流操作缺点_数据_10

 

如果需要获取最大和最小值,可以使用 max 和 min 方法。方法签名:

Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);

基本使用

Stream 流中的 max 和 min 相关方法基本使用的代码如:

@Test
    public void testMax_Min() {
        // 获取最大值
        // 1, 3, 5, 6
        Optional<Integer> max = Stream.of(5, 3, 6, 1).max((o1, o2) -> o1 - o2);
        System.out.println("最大值: " + max.get());

        // 获取最小值
        // 1, 3, 5, 6
        Optional<Integer> min = Stream.of(5, 3, 6, 1).min((o1, o2) -> o1 - o2);
        System.out.println("最小值: " + min.get());
    }

Stream流的reduce方法

java stream流操作 限制最大长度 java stream流操作缺点_开发语言_11

 

如果需要将所有数据归纳得到一个数据,可以使用 reduce 方法。方法签名:

T reduce(T identity, BinaryOperator<T> accumulator);

基本使用

Stream 流中的 reduce 相关方法基本使用的代码如:

@Test
    public void testReduce() {
        // T reduce(T identity, BinaryOperator<T> accumulator);
        // T identity: 默认值
        // BinaryOperator<T> accumulator: 对数据进行处理的方式
        // reduce如何执行?
        // 第一次, 将默认值赋值给x, 取出集合第一元素赋值给y
        // 第二次, 将上一次返回的结果赋值x, 取出集合第二元素赋值给y
        // 第三次, 将上一次返回的结果赋值x, 取出集合第三元素赋值给y
        // 第四次, 将上一次返回的结果赋值x, 取出集合第四元素赋值给y
        int reduce = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
            System.out.println("x = " + x + ", y = " + y);
            return x + y;
        });
        System.out.println("reduce = " + reduce); // 21

        // 获取最大值
        Integer max = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
            return x > y ? x : y;
        });
        System.out.println("max = " + max);
    }

java stream流操作 限制最大长度 java stream流操作缺点_java_12

 

Stream流的map和reduce组合使用

@Test
    public void testMapReduce() {
        // 求出所有年龄的总和
        // 1.得到所有的年龄
        // 2.让年龄相加
        Integer totalAge = Stream.of(
                new Person("刘德华", 58),
                new Person("张学友", 56),
                new Person("郭富城", 54),
                new Person("黎明", 52))
                .map((p) -> p.getAge()).reduce(0, Integer::sum);

        System.out.println("totalAge = " + totalAge);


        // 找出最大年龄
        // 1.得到所有的年龄
        // 2.获取最大的年龄
        Integer maxAge = Stream.of(
                new Person("刘德华", 58),
                new Person("张学友", 56),
                new Person("郭富城", 54),
                new Person("黎明", 52))
                .map(p -> p.getAge())
                .reduce(0, Math::max);
        System.out.println("maxAge = " + maxAge);

        // 统计 a 出现的次数
        //                          1    0     0    1    0    1
        Integer count = Stream.of("a", "c", "b", "a", "b", "a")
                .map(s -> {
                    if (s == "a") {
                        return 1;
                    } else {
                        return 0;
                    }
                })
                .reduce(0, Integer::sum);
        System.out.println("count = " + count);
    }

Stream流的mapToInt

如果需要将 Stream 中的 Integer 类型数据转成 int 类型,可以使用 mapToInt 方法。方法签名:

IntStream mapToInt(ToIntFunction<? super T> mapper);

java stream流操作 限制最大长度 java stream流操作缺点_java_13

 

基本使用

Stream 流中的 mapToInt 相关方法基本使用的代码如:

@Test
    public void testNumericStream() {
        // Integer占用的内存比int多,在Stream流操作中会自动装箱和拆箱
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
        // 把大于3的打印出来
        // stream.filter(n -> n > 3).forEach(System.out::println);

        // IntStream mapToInt(ToIntFunction<? super T> mapper);
        // IntStream: 内部操作的是int类型的数据,就可以节省内存,减少自动装箱和拆箱
        /*IntStream intStream = Stream.of(1, 2, 3, 4, 5).mapToInt((Integer n) -> {
            return n.intValue();
        });*/

        IntStream intStream = Stream.of(1, 2, 3, 4, 5).mapToInt(Integer::intValue);
        intStream.filter(n -> n > 3).forEach(System.out::println);
    }

Stream流的concat方法

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

备注:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。

该方法的基本使用代码如:

@Test
    public void testContact() {
        Stream<String> streamA = Stream.of("张三");
        Stream<String> streamB = Stream.of("李四");

        // 合并成一个流
        Stream<String> newStream = Stream.concat(streamA, streamB);
        // 注意:合并流之后,不能操作之前的流啦.
        // streamA.forEach(System.out::println);

        newStream.forEach(System.out::println);
    }

四:Stream综合案例

        现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的 for 循环(或增强 for 循环) 依次 进行以下若干操作步骤:

1. 第一个队伍只要名字为 3 个字的成员姓名;

2. 第一个队伍筛选之后只要前 3 个人;

3. 第二个队伍只要姓张的成员姓名;

4. 第二个队伍筛选之后不要前 2 个人;

5. 将两个队伍合并为一个队伍;

6. 根据姓名创建 Person 对象;

7. 打印整个队伍的 Person 对象信息。

两个队伍(集合)的代码如下:

public class DemoArrayListNames {
public static void main(String[] args) {
List<String> one = List.of("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子", "洪七
公");
List<String> two = List.of("古力娜扎", "张无忌", "张三丰", "赵丽颖", "张二狗", "张天爱",
"张三");
// ....
}
}

而 Person 类的代码为:

public class Person {
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

传统方式

使用 for 循环 , 示例代码 :

public class DemoArrayListNames {
public static void main(String[] args) {
List<String> one = List.of("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子", "洪七
公");
List<String> two = List.of("古力娜扎", "张无忌", "张三丰", "赵丽颖", "张二狗", "张天爱",
"张三");
// 第一个队伍只要名字为3个字的成员姓名;
List<String> oneA = new ArrayList<>();
for (String name : one) {
if (name.length() == 3) {
oneA.add(name);
}
}
// 第一个队伍筛选之后只要前3个人;
List<String> oneB = new ArrayList<>();
for (int i = 0; i < 3; i++) {
oneB.add(oneA.get(i));
}
// 第二个队伍只要姓张的成员姓名;
List<String> twoA = new ArrayList<>();
for (String name : two) {
if (name.startsWith("张")) {
twoA.add(name);
}
}
// 第二个队伍筛选之后不要前2个人;
List<String> twoB = new ArrayList<>();
for (int i = 2; i < twoA.size(); i++) {
twoB.add(twoA.get(i));
}
// 将两个队伍合并为一个队伍;
List<String> totalNames = new ArrayList<>();
totalNames.addAll(oneB);
totalNames.addAll(twoB);
// 根据姓名创建Person对象;
List<Person> totalPersonList = new ArrayList<>();
for (String name : totalNames) {
totalPersonList.add(new Person(name));
}
// 打印整个队伍的Person对象信息。
for (Person person : totalPersonList) {
System.out.println(person);
}
}
}

运行结果为:

Person{name='宋远桥'}
Person{name='苏星河'}
Person{name='洪七公'}
Person{name='张二狗'}
Person{name='张天爱'}
Person{name='张三'}

Stream方式

等效的 Stream 流式处理代码为:

public class DemoStreamNames {
public static void main(String[] args) {
List<String> one = List.of("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子", "洪七
公");
List<String> two = List.of("古力娜扎", "张无忌", "张三丰", "赵丽颖", "张二狗", "张天爱",
"张三");
// 第一个队伍只要名字为3个字的成员姓名;
// 第一个队伍筛选之后只要前3个人;
Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);
// 第二个队伍只要姓张的成员姓名;
// 第二个队伍筛选之后不要前2个人;
Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);
// 将两个队伍合并为一个队伍;
// 根据姓名创建Person对象;
// 打印整个队伍的Person对象信息。
Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);
}
}

运行效果完全一样:

Person{name='宋远桥'}
Person{name='苏星河'}
Person{name='洪七公'}
Person{name='张二狗'}
Person{name='张天爱'}
Person{name='张三'}