1. Stream流

数据流

  • 用来对集合数据做处理的

1.1. 案例需求

按照下面的要求完成集合的创建和遍历

  • 创建一个集合,存储多个字符串元素
  • 把集合中所有以"张"开头的元素存储到一个新的集合
  • 把"张"开头的集合中的长度为3的元素存储到一个新的集合
  • 遍历上一步得到的集合

1.2. 原始方式示例代码

ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1,"张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤");

//遍历list1把以张开头的元素添加到list2中。
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
    if(s.startsWith("张")){
        list2.add(s);
    }
}
//遍历list2集合,把其中长度为3的元素,再添加到list3中。
ArrayList<String> list3 = new ArrayList<>();
for (String s : list2) {
    if(s.length() == 3){
        list3.add(s);
    }
}
for (String s : list3) {
    System.out.println(s);
}     

1.3. 流的实现方式

ArrayList<String> list1 = new ArrayList<>(List.of("张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤"));

//1 获取流  list1.stream()

//2 过滤 姓张的数据   filter(s -> s.startsWith("张"))
//3 过滤 长度为3的数据  filter(s -> s.length() == 3)

//4 打印  终结操作   forEach(s -> System.out.println(s))

list1.stream().filter(s -> s.startsWith("张")).
    filter(s -> s.length() == 3).
    forEach(s -> System.out.println(s));

1.3.1. Stream流的思想

  • 类似流水线,在过程中可以多次处理流数据

1.3.2. Stream流的三类方法

  • 1获取Stream流
    • 创建一条流水线,并把数据放到流水线上准备进行操作
  • 2中间方法
    • 流水线上的操作
    • 一次操作完毕之后,还可以继续进行其他操作
  • 3终结方法
    • 一个Stream流只能有一个终结方法
    • 是流水线上的最后一个操作

1.3.3. 生成Stream流的方式

  • 1 Collection体系集合(List Set)
    • 使用默认方法stream()生成流
  • 2 Map体系集合
    • 把Map转成Set集合(keySet,entrySet),间接的生成流
    • keySet().stream() 或者 entrySet().stream()
  • 3 数组
    • 通过Arrays中的静态方法stream生成流 Arrays.stream(arr)
  • 4 同种数据类型的多个数据
    • 通过Stream接口的静态方法of(T... values)生成流 , 原理还是数组变流

1.3.4. 代码演示

//1Collection体系集合 都用stream方法
Collection<Integer> coll = new TreeSet<>();
Stream<Integer> stream = coll.stream();

//2 Map
Map<String ,String> map = new HashMap<>();
//获取所有的key的set集合 再用set集合获取流
Set<String> keys = map.keySet();
Stream<String> stream1 = keys.stream();
//获取所有的键值对的的set集合 再用set集合获取流
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream2 = entries.stream();

//3 数组
int[] arr = new int[]{3,4,5,7,8};
//用Arrays工具类获取流对象
IntStream stream3 = Arrays.stream(arr);

//4 既然数组可以, 可变参数本质是数组
//Stream.of方法就是可以把同类型的多个数据转为流对象, 内部就是数组转流
Stream<String> stream4 = Stream.of("aaa", "bbb", "ccc");

1.4. 中间操作方法

1.4.1. 概念

  • 中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作

1.4.2. 常见方法

方法名 说明
Stream\ filter(Predicate predicate) 用于对流中的数据进行过滤
Stream\ limit(long maxSize) 返回流中最前面 指定参数个数的数据组成的流
Stream\ skip(long n) 跳过指定参数个数的数据,返回由该流的剩余元素组成的流
static \ Stream\ concat(Stream a, Stream b) 合并a和b两个流为一个流
Stream\ distinct() 返回 去掉流数据中 重复的元素后剩余数据组成的流

1.4.3. filter代码

  • 流的操作不影响原集合的数据
ArrayList<String> al = new ArrayList<String>();
Collections.addAll(al, "周润发", "成龙", "小明", "刘德华", "吴京", "周星驰", "李连杰");

//流的操作不影响原集合的数据
al.stream().filter(s -> s.length() >= 3).forEach(s -> System.out.println(s));

//会把流数据里的每一条数据 传到test方法中 获取返回值,返回为true的数据留下,为false的删除
al.stream().filter(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.length() == 3;
    }
});

1.4.4. limit skip代码

ArrayList<String> al = new ArrayList<String>(
    List.of("周润发", "成龙", "小明", "刘德华", "吴京", "周星驰", "李连杰"));
//limit从前面获取指定数量的数据
al.stream().limit(5).forEach(s -> System.out.println(s));
System.out.println("----------------------------------");
//跳过前3个 获取后面的所有数据
al.stream().skip(3).forEach(s -> System.out.println(s));
System.out.println("----------------------------------");

//获取前5个 然后跳过前3个
al.stream().limit(5).skip(3).forEach(s -> System.out.println(s));

1.4.5. concat 和distinct

//  static <T> Stream<T> concat(Stream a, Stream b) 合并a和b两个流为一个流
ArrayList<String> al1 = new ArrayList<String>();
Collections.addAll(al1, "林心如", "张曼玉", "林青霞", "柳岩", "林志玲", "小明", "王祖贤");
Stream.concat(al.stream(), al1.stream()).forEach(s -> System.out.println(s));

System.out.println("--------------------------------------------------------------");
//  Stream<T> distinct()         返回 去掉流数据中 重复的元素后剩余数据组成的流
Stream.concat(al.stream(), al1.stream()).distinct().forEach(s -> System.out.println(s));

1.5. 终结操作方法

1.5.1. 概念

  • 终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作

1.5.2. 常见方法

方法名 说明
void forEach(Consumer action) 对此流的每个元素执行操作
long count() 返回此流中的元素数

1.5.3. 代码

ArrayList<String> al = new ArrayList<String>();
Collections.addAll(al,"周润发", "成龙", "小明", "刘德华", "吴京", "周星驰", "李连杰")

al.stream().skip(2).limit(2).forEach(s -> {
    System.out.println(s);
});

long count = al.stream().skip(2).count();//获取数据的数量
System.out.println(count);

1.5.4. collect收集操作

  • collect方法 获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
  • Collectors提供了具体的收集方式
    • 收集到List 、收集到Set、收集到Map
方法名 说明
public static \ Collector toList() 把元素收集到List集合中
public static \ Collector toSet() 把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中

1.5.5. 收集到list和set的代码

  • 把集合转为流,然后把偶数留下,收集到新的集合中(list或者set)返回
public static void main(String[] args) {
    ArrayList<Integer> al = new ArrayList<>();
    for (int i = 1; i <= 10; i++) {
        al.add(i);
    }
    //添加一些重复数据  为了后面set集合演示去重复
    al.add(10);
    al.add(10);
    al.add(10);
    al.add(10);

    //使用流 获取所有的偶数 最后collect收集到List集合里
    List<Integer> list = al.stream().filter(number -> number % 2 == 0).collect(Collectors.toList());
    System.out.println(list);

    //使用流 获取所有的偶数 最后collect收集到set集合中返回,这里返回的是set集合,重复的数据最后会默认去除
    Set<Integer> set = al.stream().filter(number -> number % 2 == 0).collect(Collectors.toSet());
    System.out.println(set);
}

1.5.6. 收集到map

  • 把学生的名字作为key , 年龄作为value
package com.heima.test5;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Demo05 {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<String>(List.of("001,周润发", "002,成龙", "003,小明", "004,刘德华", "005,吴京"));
       //把集合的数据 存到map里 序号作为key 人名作为值  {"001":"周润发","002":"成龙"}

        Map<String, String> map = al.stream().collect(Collectors.toMap(
                new Function<String, String>() {
                    @Override
                    public String apply(String s) {
                        //返回值作为key   一般会通过处理s得到
                        return s.split(",")[0];
                    }
                }, new Function<String, String>() {
                    @Override
                    public String apply(String s) {
                        //返回值value 一般会通过处理s得到
                        return s.split(",")[1];
                    }
                }
        ));

        //lambda写法  
        Map<String, String> map = al.stream().collect(
                Collectors.toMap(
                        //返回key  "001,周润发"  逗号分割 第0个数据返回作为key
                        s -> s.split(",")[0],
                        ///返回value  "001,周润发" 逗号分割 第1个数据返回作为value
                        s -> s.split(",")[1]
                )
        );

        System.out.println(map);

    }
}

1.6. 综合案例

1.6.1. 案例需求

  • 现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作
    • 男演员只要名字为3个字的前三人
    • 女演员只要姓林的,并且不要第一个
    • 把过滤后的男演员姓名和女演员姓名合并到一起
    • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
  • 演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法

1.6.2. 演员类代码

public class Actor {
    private String name;

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

    public String getName() {
        return name;
    }

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

1.6.3. 测试类代码

public static void main(String[] args) {
    ArrayList<String> manList = new ArrayList<String>();
    Collections.addAll(manList, "周润发", "成龙", "刘德华", "吴京", "周星驰", "李连杰");
    ArrayList<String> womanList = new ArrayList<String>();
    Collections.addAll(womanList, "林心如", "张曼玉", "林青霞", "柳岩", "林志玲", "王祖贤");

    //名字为3个字的前三人
    Stream<String> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);
    //只要姓林的,并且不要第一个
    Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);

    //合并过滤后的男演员和女演员
    Stream<String> newStream = Stream.concat(manStream, womanStream);
    newStream.forEach(s -> {
        //创建演员对象 把名字传入
        Actor actor = new Actor(s);
        //获取演员的对象的名字打印
        System.out.println(actor.getName());
    });
}

1.7. flatMap

  • map 对管道流中的每一个数据元素进行转换操作。
ArrayList<String> list = new ArrayList<>();
list.add("zhangsan,15");
list.add("lisi,18");
list.add("wangwu,26");
list.add("xiaoming,19");

//map中间操作 转换功能 把流中的数据 经过处理 转为另外一种类型的数据
list.stream().map(new Function<String, Student>() {
    @Override //zhangsan,15
    public Student apply(String s) {
        //1 s用逗号切割
        String[] ss = s.split(",");
        //2 生成学生对象  注意年龄转为int
        Student stu = new Student(ss[0], Integer.parseInt(ss[1]));

        return stu;
    }
}).forEach(s -> System.out.println(s)); 
  • flatMap
   public static void main(String[] args) {
        ArrayList<Student> list1 = new ArrayList<>();
        list1.add(new Student("zs", 20));
        list1.add(new Student("lisi", 22));
        list1.add(new Student("wangwu", 21));

        ArrayList<Student> list2 = new ArrayList<>();
        list2.add(new Student("xiaohong", 20));
        list2.add(new Student("xiaolan", 19));
        list2.add(new Student("xiaofang", 21));

        ArrayList<  ArrayList<Student> > list = new ArrayList<>();
        //注意 list集合里装的是集合
        list.add(list1);
        list.add(list2);
        System.out.println(list);

        //[[zs--20, lisi--22, wangwu--21], [xiaohong--20, xiaolan--19, xiaofang--21]]
        //把list的里的所有学生 全部放到一个流对象里面
        //过滤所有学生 保留年龄大于20的
        list.stream().flatMap(new Function<   ArrayList<Student>, Stream<Student>  >() {
            @Override
            public Stream<Student> apply(ArrayList<Student> students) {
                return students.stream();
            }
        }).filter(stu -> stu.age > 20).forEach(stu -> System.out.println(stu));

    }
  • flatMap和map的区别

    map是把数据处理完 转为其他类型的对象
    flatMap是把不同数据里的集合里的数据拿出来,合并放一个集合中

1.7.1. 案例

  • 有user类 ,衣服类, 一个user对象 可以有多个衣服对象

import java.util.List;

//衣服
class Clothes {
    String name;

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

//用户
public class User {
    String name;
    List<Clothes> cs;

    public User(String name, List<Clothes> cs) {
        this.name = name;
        this.cs = cs;
    }
}
  • 初始数据
public static void main(String[] args) {
    Clothes c3 = new Clothes("李宁羽绒服");
    Clothes c1 = new Clothes("阿玛尼背心");
    Clothes c2 = new Clothes("耐克运动裤");
    Clothes c4 = new Clothes("李宁夹克");
    Clothes c5 = new Clothes("阿迪短裤");
    Clothes c6 = new Clothes("安踏篮球裤");
    Clothes c7 = new Clothes("李宁帽子");

    ArrayList<Clothes> list01 = new ArrayList<>();
    ArrayList<Clothes> list02 = new ArrayList<>();
    Collections.addAll(list01, c1, c2, c3, c4);
    Collections.addAll(list02, c5, c6, c7);

    User u01 = new User("zs", list01);
    User u02 = new User("lisi", list02);

    ArrayList<User> users = new ArrayList<>();
    users.add(u01);
    users.add(u02);
}
  • 把所有用户的衣服合并到一个集合中
如人饮水,冷暖自知。
最后更新于 2023-08-05