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 表达式进行替换,或使用方法引用替换(下期介绍)。

相关推荐
啾啾Fun17 分钟前
Java反射操作百倍性能优化
java·性能优化·反射·缓存思想
20岁30年经验的码农24 分钟前
若依微服务Openfeign接口调用超时问题
java·微服务·架构
曲莫终33 分钟前
SpEl表达式之强大的集合选择(Collection Selection)和集合投影(Collection Projection)
java·spring boot·spring
ajassi20001 小时前
开源 java android app 开发(十二)封库.aar
android·java·linux·开源
q567315231 小时前
Java使用Selenium反爬虫优化方案
java·开发语言·分布式·爬虫·selenium
kaikaile19951 小时前
解密Spring Boot:深入理解条件装配与条件注解
java·spring boot·spring
守护者1701 小时前
JAVA学习-练习试用Java实现“一个词频统计工具 :读取文本文件,统计并输出每个单词的频率”
java·学习
bing_1581 小时前
Spring Boot 中ConditionalOnClass、ConditionalOnMissingBean 注解详解
java·spring boot·后端
ergdfhgerty1 小时前
斐讯N1部署Armbian与CasaOS实现远程存储管理
java·docker
勤奋的知更鸟2 小时前
Java性能测试工具列举
java·开发语言·测试工具