Stream 流常见基本操作

文章目录

概述

Stream 流是 Java8 新特性之一,我们在实际开发中借助 Stream 流搭配 Lambda 表达式,可以很方便的完成一些对集合的操作,可以显著的提升开发的效率和性能。

一、Stream 流的常见生成方式

通常我们会选择对以下类型生成 Stream 流:单列集合、双列集合、数组、零散的数据。

c 复制代码
public class StreamDemo {
    public static void main(String[] args) {
        //Collection体系的集合可以使用默认方法stream()生成流
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<String>();
        Stream<String> setStream = set.stream();

        //Map体系的集合间接的生成流
        Map<String,Integer> map = new HashMap<String, Integer>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<Integer> valueStream = map.values().stream();
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        //数组可以通过Arrays中的静态方法stream生成流
        String[] strArray = {"hello","world","java"};
        Stream<String> strArrayStream = Arrays.stream(strArray);
      
      	//同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流
        Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
        Stream<Integer> intStream = Stream.of(10, 20, 30);
    }
}

二、Stream 流中间操作方法

1、常用中间操作方法

这里的中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作(其他中间操作或终结操作)

方法名 说明
Stream filter(Predicate predicate) 用于对流中的数据进行过滤
Stream limit(long maxSize) 获取前几个元素
Stream skip(long n) 跳过前几个元素
Stream distinct() 元素去重,依赖(hashCode和equals方法)
Stream sorted(Comparator<? super T> comparator) Stream 流元素排序
static Stream concat(Stream a, Stream b) 合并a和b两个流为一个流
Stream map(Function<T,R> mapper) 转换流中的数据类型

Stream.contact 合并两个 stream 流的时候,如果一个 stream 是 a 类型,另一个 stream 是 b 类型,那么最终合并为 a,b的父类型(类型提升)。

2、使用示例1

c 复制代码
public class FilterDemo {
	// 获取以张开头的元素
    public static void main(String[] args) {
    	 // 创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        list.stream().filter(s ->s.startsWith("张")).forEach(s-> System.out.println(s));

    }
}

3、使用示例2

c 复制代码
public class LimitAndSkipDemo {
    public static void main(String[] args) {
        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<String>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

        //需求1:取前3个数据在控制台输出
        list.stream().limit(3).forEach(s-> System.out.println(s));
        System.out.println("--------");

        //需求2:跳过3个元素,把剩下的元素在控制台输出
        list.stream().skip(3).forEach(s-> System.out.println(s));
        System.out.println("--------");

        //需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
        list.stream().skip(2).limit(2).forEach(s-> System.out.println(s));
    }
}

4、使用示例3

c 复制代码
public class SortedDemo {
	// 按照年龄进行排序
    public static void main(String[] args) {
        ArrayList<String> manList = new ArrayList<>();
        Collections.addAll(manList,"蔡坤坤,24","叶厚贤,23,","刘不甜,22","吴倩,24","谷加,30","肖亮亮,27");

        manList.stream()
                .sorted((o1,o2)->Integer.parseInt(o1.split(",")[1]) - Integer.parseInt(o2.split(",")[1]))
                .forEach(x -> System.out.println(x));
    }
}

5、使用示例4

c 复制代码
public class ConcatAndDistinctDemo {
    public static void main(String[] args) {
        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<String>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

        //需求1:取前4个数据组成一个流
        Stream<String> s1 = list.stream().limit(4);

        //需求2:跳过2个数据组成一个流
        Stream<String> s2 = list.stream().skip(2);

        //需求3:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
        Stream.concat(s1,s2).distinct().forEach(s-> System.out.println(s));
    }
}

6、使用示例5

c 复制代码
public class MapDemo {
    public static void main(String[] args) {

        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张无忌-15","周芷若-14","赵敏-13","张强-20","张三丰-100","张翠山-40");

        // 需求:只获取里面1的年龄并进行打印
        List<Integer> collect = list.stream()
                .map(s -> Integer.parseInt(s.split("-")[1]))
                .collect(Collectors.toList());
        System.out.println(collect);

    }
}

7、Stream 流使用注意事项

注意1 :修改 Stream 流中的数据,不会影响原来集合或者数组中的数据。

注意2 :中间方法,返回新的 Stream 流,原来的 Stream 流只能使用一次,建议使用链式编程()。因为每次调用中间方法都会返回新的 Stream 流,原来的 Stream 流会关闭。如:

c 复制代码
public class Test1 {
    public static void main(String[] args) {

        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<String>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

        Stream<String> stream1 = list.stream().filter(s -> s.startsWith("张"));
        Stream<String> stream2 = stream1.filter(s -> s.length() == 3);
        stream2.forEach(s -> System.out.println(s));

        stream1.filter(s -> s.length() == 2);
    }
}

三、Stream 流终结操作方法

1、常用终结方法

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

方法名 说明
void forEach(Consumer action) 遍历
long count() 统计个数
toArray() 收集流中的数据,放到数组中
R collect(Collector collector) 收集流中的数据,放到集合中

工具类 Collectors 提供了具体的收集方式:

方法名 说明
public static Collector toList() 把元素收集到List集合中
public static Collector toSet() 把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中

注意1toSet() 会进行自动去重。
注意2toMap() 如果我们要收集到 Map 集合当中,键重复会报错。

2、使用示例1

c 复制代码
public class CountAndForEachDemo {
    public static void main(String[] args) {
    
        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");


        // 统计返回此流中的元素数
        long count = list.stream().count();
        System.out.println(count);

        // 遍历打印流中元素
        list.stream().forEach(x -> System.out.println(x));

    }
}

3、使用示例2

c 复制代码
public class ToArrayDemo {
    public static void main(String[] args) {
    
        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        // 收集流中的数据,放到数组中 (Object 数组)
        Object[] objects = list.stream().toArray();
        System.out.println(Arrays.toString(objects));

        // 收集流中的数据,放到数组中 (指定类型 数组)
        // toArray 方法参数就是创建一个指定类型的数组
        // 底层会依次得到流里面的每一个数据,并把数据放到数组当中
        // 返回值就是一个装着流里面所有数据的数组
        String[] strings = list.stream().toArray(valuecount -> new String[valuecount]);
        System.out.println(Arrays.toString(strings));

    }
}

4、使用示例3

c 复制代码
public class CollectDemo {
    public static void main(String[] args) {

        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张无忌-男-15","周芷若-女-14","赵敏-女-13","张三丰-男-100","谢广坤-男-41");

        // 需求1:把所有男性收集到list集合当中
        List<String> newList1 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toList());
        System.out.println(newList1);

        // 需求2:把所有男性收集到set集合当中
        Set<String> newList2 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toSet());
        System.out.println(newList2);

        // 需求3:把所有男性收集到map集合当中(键为姓名,值为年龄)
        Map<String, Integer> map = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toMap(
                        s -> s.split("-")[0],
                        s -> Integer.parseInt(s.split("-")[2])));
        System.out.println(map);
    }
}

5、Stream 基本分组功能

方法名 说明
Collector<T, ?, Map<K, List>> groupingBy(Function<? super T, ? extends K> classifier) 这是最基本的 groupingBy 方法,classifier:键映射:该方法的返回值是键值对的 键
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) 这个重载方法在基本的 groupingBy 方法的基础上添加了一个 downstream 参数,downstream:值映射:通过聚合方法将同键下的结果聚合为指定类型,该方法返回的是键值对的 值
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier mapFactory, Collector<? super T, A, D> downstream) 这个重载方法除了包含分类函数和 downstream 收集器外,还接收一个 mapFactory 参数,mapFactory:无参构造函数提供返回类型:提供一个容器初始化方法,用于创建新的 Map容器 (使用该容器存放值对)
c 复制代码
public class GroupDemo {
	// 基本分组操作
    public static void main(String[] args) {
        // 初始化数据
        Actor actor1 = new Actor("张三",18);
        Actor actor2 = new Actor("李四",20);
        Actor actor3 = new Actor("王五",22);
        List<Actor> actors = new ArrayList<>();
        Collections.addAll(actors,actor1,actor2,actor3);
        // 按照姓名分组
        Map<String, List<Actor>> collect = actors.stream().collect(Collectors.groupingBy(actor -> actor.getName()));
        // 打印结果
        System.out.println(collect);

        // 将不同课程的学生进行分类
        Map<String, List<Actor>> groupByCourse = actors.stream().collect(Collectors.groupingBy(actor -> actor.getName()));
        System.out.println(groupByCourse
        );
        Map<String, List<Actor>> groupByCourse1 = actors.stream().collect(Collectors.groupingBy(actor -> actor.getName(), Collectors.toList()));
        System.out.println(groupByCourse1);

        // 上面的方法中容器类型和值类型都是默认指定的,容器类型为:HashMap,值类型为:ArrayList
        // 可以通过下面的方法自定义返回结果、值的类型
        Map<String, List<Actor>> groupByCourse2 = actors.stream()
                .collect(Collectors.groupingBy(actor -> actor.getName(),
                        () -> new HashMap<>(),
                        Collectors.toList()));
        System.out.println(groupByCourse2);
    }
}

其他分组操作参考Stream Collectors.groupingBy 的四种用法

四、Stream 综合小练习

c 复制代码
public class Test {
    public static void main(String[] args) {
        // 现有两个 ArrayList 集合:
        // 第一个集合:存储 6 名男演员的名字和年龄;第二个集合:存储 6 名女演员的名字和年龄
        ArrayList<String> manList = new ArrayList<>();
        ArrayList<String> womanList = new ArrayList<>();
        Collections.addAll(manList,"蔡坤坤,24","叶厚贤,23,","刘不甜,22","吴倩,24","谷加,30","肖亮亮,27");
        Collections.addAll(womanList,"赵晓英,35","杨颖,36","高媛媛,43","刘诗,35","杨小敏,33");

        // 需求1:男演员只要名字为 3 个字的前两人
        Stream<String> stream1 = manList.stream().filter(s -> s.split(",")[0].length() == 3)
                .limit(2);

        // 需求2:女演员只要姓杨的,并且不要第一个
        Stream<String> stream2 = womanList.stream().filter(s -> s.split(",")[0].startsWith("杨"))
                .skip(1);

        // 需求3:把过滤后的男演员姓名和女演员合并到一起封装成 Actor 对象(Actor 对象属性有 name、age)
        Stream<Actor> stream3 = Stream.concat(stream1, stream2)
                .map(s -> new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1])));

        // 需求5:将所有的演员对象都报存到 List 集合中
        stream3.collect(Collectors.toList()).forEach(s -> System.out.println(s));

    }
}

小结

Stream 流的使用步骤?

  1. 获取 Stream 流对象
  2. 使用中间方法处理数据
  3. 使用终结方法处理数据

如何获取 Stream 流对象?

  1. 单列集合:Collection 中的默认方法
  2. 双列集合:不能直接获取,可借助 keySets、values 转换成单列集合间接获取
  3. 数组:Arrays 工具类型中的静态方法
  4. 零散的数据:Stream 接口中的静态方法 of

Stream 常见方法?

  1. 中间方法:filter、limit、skip、distinct、concat、map
  2. 终结方法:forEach、count、collect

Lambda 怎么简化?

  1. 首先,数据类型可以省略
  2. 如果形参只有一个,小括号可以省略
  3. 如果形参有多个,小括号就不能省略了
  4. 如果方法体只有一行,那么大括号可以省略,return 可以省略分号可以省略

:上述所有方法皆为函数式接口,因此均可以使用 Lambda 表达式进行替换,或使用方法引用替换(下期介绍)。

相关推荐
阿龟在奔跑35 分钟前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF36 分钟前
m个数 生成n个数的所有组合 详解
java·递归
代码小鑫1 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖1 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶1 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
周全全1 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
uzong2 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端
AiFlutter2 小时前
Java实现简单的搜索引擎
java·搜索引擎·mybatis
飞升不如收破烂~2 小时前
Spring boot常用注解和作用
java·spring boot·后端
秦老师Q2 小时前
Java基础第九章-Java集合框架(超详细)!!!
java·开发语言