文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java-Stream详解

2023-09-13 16:27

关注

Stream 定义

Stream是Java 8新增的接口,Stream可以认为是一个高级版本的 Iterator。

Stream跟Iterator的差别

名称含义
无存储Stream是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。
函数式编程函数式编程:对Stream的任何修改都不会修改背后的数据源,比如对Stream执行filter操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新的Stream。
延迟执行Stream的操作由零个或多个中间操作(intermediate operation)和一个结束操作(terminal operation)两部分组成。只有执行了结束操作,Stream定义的中间操作才会依次执行,这就是Stream的延迟特性。
可消费性Stream只能被“消费”一次,一旦遍历过就会失效。就像容器的迭代器那样,想要再次遍历必须重新生成一个新的Stream。

Stream的操作是建立在函数式接口的组合之上的

Java8中新增的函数式接口都在java.util.function包下。这些函数式接口可以有多种分类方式。其中B开头的是二元函数,其他是一元函数

名称函数示例
一般函数Function,BiFunction例如:get方法
算子函数UnaryOperator,BinaryOperator例如:stream.reduce方法的参数
谓词函数Predicate,BiPredicate例如:stream.filter方法的参数
消费者Consumer,BiConsumer例如:set方法
供应者Supplier,BiSupplier例如:get方法

Stream的操作符

流的操作类型主要分为两种:中间操作符(Intermediate)、终端操作符(Terminal)

  1. Stream的操作由零个或多个中间操作(intermediate operation)和一个结束操作(terminal operation)两部分组成。只有执行了结束操作,Stream定义的中间操作才会依次执行,这就是Stream的延迟特性。

  2. 中间操作只是将流从一个流变成了另一个流,结束操作会生成对应的数据

  3. 这里无状态即无序,短路操作和非短路操可以理解为对应的算法操作

操作分类名称含义示例
中间操作符无状态指元素的处理不受之前元素的影响unordered() filter() map() mapToint() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
中间操作符有状态指该操作只有拿到所有元素之后才能继续下去distinct() sorted() limit() skip()
终端操作符非短路操作指必须处理所有元素才能得到最终结果forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
终端操作符短路操作指遇到某些符合条件的元素就可以得到最终结果,如 A或B,只要A为true,则无需判断B的结果anyMatch() allmatch() noneMatch() findFirst() findAny()

Stream的使用

对 Stream 的使用可以分为3步:

一、创建 Stream

1、使用Collection下的 stream() 和 parallelStream() 方法

List list = new ArrayList<>();Stream stream = list.stream(); //获取一个顺序流Stream parallelStream = list.parallelStream(); //获取一个并行流

2、使用Arrays 中的 stream() 方法,将数组转成流

Integer[] nums = new Integer[10];Stream stream = Arrays.stream(nums);

3、使用Stream中的静态方法:of()、iterate()、generate()

Stream stream = Stream.of(1,2,3,4,5,6);Stream stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);  // 不使用limit会无限生成Stream stream3 = Stream.generate(Math::random).limit(2); // 不使用limit会无限生成

4、使用 BufferedReader.lines() 方法,将每行内容转成流

BufferedReader reader = new BufferedReader(new FileReader("D:\\stream.txt"));Stream lineStream = reader.lines();lineStream.forEach(System.out::println);

5、使用 Pattern.splitAsStream() 方法,将字符串分隔成流

Pattern pattern = Pattern.compile(",");Stream stringStream = pattern.splitAsStream("a,b,c,d");stringStream.forEach(System.out::println);

二、Intermediate(可以多次):通过一系列中间操作,对数据集进行过滤、检索等数据集的再次处理。

1、筛选与切片

名称含义参数类型
filter过滤流中的某些元素Predicate
limit(n)获取n个元素long
skip(n)跳过n元素,配合limit(n)可实现分页long
distinct通过流中元素的 hashCode() 和 equals() 去除重复元素
// list中大于6且小于9的元素个数List list = Arrays.asList(7, 6, 9, 3, 8, 2, 1, 2);long count = list.stream().filter(x -> x > 6&&x < 9).count();System.out.println("list中大于6且小于9的元素个数:" + count);// 对流进行过滤去重,跳过和限制获取Stream newStream  = list.stream().filter(e -> e > 1) // 7, 6, 9, 3, 8, 2, 2.distinct() // 7, 6, 9, 3, 8, 2.skip(2)   //  9, 3, 8, 2.limit(2); //  9, 3

2、映射

名称含义参数类型
map接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素Function
flatMap接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流,及扁平化流,降低集合或对象的复杂程度,就像下面的例子一样把String[] 降级为String,然后在进行操作Function> T表示映射对象的类型,R表示返回的值是Stream的子类型

扁平化入图所示

// mapList stringList = Arrays.asList("hello", "world");List collect = stringList.stream().map(str -> str.split("")).distinct().collect(Collectors.toList());collect.forEach(col-> System.out.println(Arrays.toString(col)));//[h, e, l, l, o]//[w, o, r, l, d]// flatMapList flatMapCollect = stringList.stream()                .map(str -> str.split(""))                .flatMap(Arrays::stream)                .distinct()                .collect(Collectors.toList());        System.out.println(flatMapCollect);//[h, e, l, o, w, r, d]

3、排序

名称含义参数类型
sorted()自然排序,流中元素需实现Comparable接口
sorted(Comparator com)定制排序,自定义Comparator排序器Comparator comparator

单个排序

// sorted()List authors = StreamDataInit.getAuthors();List defaultList = authors.stream().sorted().collect(Collectors.toList());defaultList.forEach(e -> System.out.println("Id:"+ e.getId()+", Name: "+e.getName()+", Age:"+e.getAge()));// sorted(Comparator com)defaultList = authors.stream().sorted(Comparator.comparing(Author::getAge).reversed()).collect(Collectors.toList());defaultList.forEach(e -> System.out.println("Id:"+ e.getId()+", Name: "+e.getName()+", Age:"+e.getAge()));

多个排序

//方法1:先对年龄进行升序,升序结果进行反转,再对班级进行降序userList = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getClassNo, Comparator.reverseOrder())).collect(Collectors.toList());//方法2:直接对年龄进行降序,再对班级进行降序userList = userList.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder()).thenComparing(User::getClassNo, Comparator.reverseOrder())).collect(Collectors.toList());//方式3:先对年龄进行升序,再对班级进行升序,最后对结果进行反转userList = userList.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getClassNo).reversed()).collect(Collectors.toList());

总结: sorted的参数

  1. 如果只进行单个字段的升序降序排列,我们使用reversed() 或者 Comparator.reverseOrder() 都可以

  2. 如果要进行多个字段的升序降序排列,我们还是使用 Comparator.reverseOrder() 会更优雅、更好理解一些

  3. 参数形式速记

4、消费

Student s1 = new Student("aa", 10);Student s2 = new Student("bb", 20);List studentList = Arrays.asList(s1, s2);  studentList.stream().peek(o -> o.setAge(100)).forEach(System.out::println);  //结果:Student{name='aa', age=100}Student{name='bb', age=100}

三、Terminal(只有一次):通过最终(terminal)方法完成对数据集中元素的最终处理。

1、 匹配、聚合操作

名称含义参数类型
allMatch接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回falsePredicate
noneMatch接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回falsePredicate
anyMatch接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回falsePredicate
findFirst返回流中第一个元素
findAny返回流中的任意元素
count返回流中元素的总个数
max返回流中元素最大值Comparator
min返回流中元素最小值Comparator
List list = Arrays.asList(1, 2, 3, 4, 5);  boolean allMatch = list.stream().allMatch(e -> e > 10); //falseboolean noneMatch = list.stream().noneMatch(e -> e > 10); //trueboolean anyMatch = list.stream().anyMatch(e -> e > 4); //true  Integer findFirst = list.stream().findFirst().get(); //1Integer findAny = list.stream().findAny().get(); //1  long count = list.stream().count(); //5Integer max = list.stream().max(Integer::compareTo).get(); //5Integer min = list.stream().min(Integer::compareTo).get(); //1 

2、 规约操作

规约操作又被称作折叠操作,是通过某个连接动作将所有元素汇总成一个汇总结果的过程。元素求和、求最大值或最小值、求出元素总个数、将所有元素转换成一个列表或集合,都属于规约操作。_Stream_类库有两个通用的规约操作reduce()和collect(),也有一些为简化书写而设计的专用规约操作,比如sum()、max()、min()、count()等。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

 List authors = StreamDataInit.getAuthors(); Integer sum = authors.stream()         .distinct()         .map(author -> author.getAge())         .reduce(0, (result, element) -> result + element); System.out.println(sum);  Integer reduce1 = authors.stream()         .map(author -> author.getAge())         .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result); System.out.println(reduce1);  Integer reduce2 = authors.stream()         .map(author -> author.getAge())         .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result); System.out.println(reduce2);  Optional reduce = authors.stream()         .map(author -> author.getAge())         .reduce(new BinaryOperator() {             @Override             public Integer apply(Integer result, Integer element) {                 return result > element ? element : result;             }         }); reduce.ifPresent(age -> System.out.println(age));  List list = Arrays.asList(1, 3, 2, 8, 11, 4); // 求和方式1 Optional sum1 = list.stream().reduce((x, y) -> x + y); // 求和方式2 Optional sum2 = list.stream().reduce(Integer::sum); // 求和方式3 Integer sum3 = list.stream().reduce(0, Integer::sum); System.out.println("list求和:" + sum1.get() + "," + sum2.get() + "," + sum3); // 求和方式4 Integer sum4 = authors.stream().collect(Collectors.reducing(0, Author::getAge, (i, j) -> (i + j - 5000))); System.out.println("作家扣税薪资总和:" + sum4); // 求和方式5 Optional sum5 = authors.stream().map(Author::getAge).reduce(Integer::sum); System.out.println("作家薪资总和:" + sum5.get()); // 求和方式6 BigDecimal sumSalary = authors.stream()         .map(Author::getSalary)         .reduce(BigDecimal.ZERO, BigDecimal::add); System.out.println("工资之和:"+sumSalary);  Optional product = list.stream().reduce((x, y) -> x * y);  // 求最大值方式1 Optional max1 = list.stream().reduce((x, y) -> x > y ? x : y); // 求最大值写法2 Integer max2 = list.stream().reduce(1, Integer::max); System.out.println("list求积:" + product.get()); System.out.println("list求和:" + max1.get() + "," + max2);

3、 收集操作

collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。Collector 是一个接口,有以下5个抽象方法:

名称含义
Supplier supplier()创建一个结果容器A
BiConsumer accumulator()消费型接口,第一个参数为容器A,第二个参数为流中元素T
BinaryOperator combiner()函数接口,该参数的作用跟上一个方法(reduce)中的combiner参数一样,将并行流中各 个子进程的运行结果(accumulator函数操作后的容器A)进行合并
Function finisher()函数式接口,参数为:容器A,返回类型为:collect方法最终想要的结果R
Set characteristics()返回一个不可变的Set集合,用来表明该Collector的特征。有以下三个特征CONCURRENT:表示此收集器支持并发。UNORDERED:表示该收集操作不会保留流中元素原有的顺序。IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。
public class CollectorCustom {    public static void main(String[] args) {        Set collect = Arrays.asList(1, 2, 3, 3, 4, 5, 6).stream().collect(new MyCollector());        System.out.println(collect);    }    public static class MyCollector implements Collector, Set> {        @Override        public Supplier> supplier() {            System.out.println("MyCollector.supplier");            return HashSet::new;  -->我们提供一个HashSet        }        @Override        public BiConsumer, T> accumulator() {            System.out.println("MyCollector.accumulator");            return Set::add; -->我们处理Set 和流中元素T的关系             }        @Override        public BinaryOperator> combiner() {            System.out.println("MyCollector.combiner");            return (st1, st2) -> {                st1.addAll(st2);                return st1;   ->如果是并发流,创建了多个容器,我们处理多个容器间的关系            };        }        @Override        public Function, Set> finisher() {            System.out.println("MyCollector.finisher");            return Function.identity();  -> 处理 容器和最终返回的规约,我们选择都是返回Set        }        @Override        public Set characteristics() {            System.out.println("MyCollector.characteristics");            return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, UNORDERED));            --> 当我们使用了 IDENTITY_FINISH ,其实就不用再写finisher();不知道你明不明白?            --> UNORDERED 不追求顺序,我们毕竟用的HashSet        }    }}

4、collect实战

(1)收集 Stream 流中的数据到集合中
//1.收集数据到list集合中stream.collect(Collectors.toList())//2.收集数据到set集合中stream.collect(Collectors.toSet())//3.收集数据到指定的集合中Collectors.toCollection(Supplier collectionFactory)stream.collect(Collectors.joining())
public class CollectDataToCollection{    public static void main(String[] args) {        //Stream 流        Stream stream = Stream.of("aaa", "bbb", "ccc", "bbb");        //收集流中的数据到集合中        //1.收集流中的数据到 list        List list = stream.collect(Collectors.toList());        System.out.println(list);        //2.收集流中的数据到 set        Set collect = stream.collect(Collectors.toSet());        System.out.println(collect);        //3.收集流中的数据(ArrayList)(不收集到list,set等集合中,而是)收集到指定的集合中        ArrayList arrayList = stream.collect(Collectors.toCollection(ArrayList::new));        System.out.println(arrayList);        //4.收集流中的数据到 HashSet        HashSet hashSet = stream.collect(Collectors.toCollection(HashSet::new));        System.out.println(hashSet);    }}
(2)收集 Stream 流中的数据到数组中
//1.使用无参,收集到数组,返回值为 Object[](Object类型将不好操作)Object[] toArray();//2.使用有参,可以指定将数据收集到指定类型数组,方便后续对数组的操作 A[] toArray(IntFunction generator);
public class CollectDataToArray{    public static void main(String[] args) {        //Stream 流        Stream stream = Stream.of("aaa", "bbb", "ccc", "bbb");                 //2.1 使用 toArray()无参        Object[] objects = stream.toArray();        for (Object o: objects) {//此处无法使用.length() 等方法            System.out.println("data:"+o);        }        //2.2 使用有参返回指定类型数组        //无参不好的一点就是返回的是 Object[] 类型,操作比较麻烦.想要拿到长度,Object是拿不到长度的        String[] strings = stream.toArray(String[]::new);        for(String str : strings){            System.out.println("data:"+str + ",length:"+str.length());        }    }}
(3)Stream流中数据聚合/分组/分区/拼接操作
//最大值Collectors.maxBy();//最小值Collectors.minBy();//总和Collectors.summingInt();/Collectors.summingDouble();/Collectors.summingLong();//平均值Collectors.averagingInt();/Collectors.averagingDouble();/Collectors.averagingLong();//总个数Collectors.counting();

聚合操作

public class CollectDataToArray{    public static void main(String[] args) {        Stream studentStream = Stream.of(                new Student("赵丽颖", 58, 95),                new Student("杨颖", 56, 88),                new Student("迪丽热巴", 56, 99),                new Student("柳岩", 52, 77)        );        //聚合操作        //获取最大值(Stream流 max()方法亦可)        //max()方法实现        //Optional max = studentStream.max((s1, s2) -> s1.getScore() - s2.getScore());        //(聚合)实现        Optional max = studentStream.collect(Collectors.maxBy((s1, s2) -> s1.getScore() - s2.getScore()));        System.out.println("最大值:"+max.get());        //获取最小值(Stream流 min()方法亦可)        //min()方法实现        //Optional min = studentStream.max((s1, s2) -> s2.getScore() - s1.getScore());        //(聚合)实现        Optional min = studentStream.collect(Collectors.minBy((s1, s2) -> s1.getScore() - s2.getScore()));        System.out.println("最小值:"+min.get());        //求总和(使用Stream流的map()和reduce()方法亦可求和)        //map()和reduce()方法实现        //Integer reduce = studentStream.map(s -> s.getAge()).reduce(0, Integer::sum);        //(聚合)简化前        //Integer ageSum = studentStream.collect(Collectors.summingInt(s->s.getAge()));        //(聚合)使用方法引用简化        Integer ageSum = studentStream.collect(Collectors.summingInt(Student::getAge));        System.out.println("年龄总和:"+ageSum);        //求平均值        //(聚合)简化前        //Double avgScore = studentStream.collect(Collectors.averagingInt(s->s.getScore()));        //(聚合)使用方法引用简化        Double avgScore = studentStream.collect(Collectors.averagingInt(Student::getScore));        System.out.println("分数平均值:"+avgScore);        //统计数量(Stream流 count()方法亦可)        //count()方法实现        //long count = studentStream.count();        //(聚合)统计数量        Long count = studentStream.collect(Collectors.counting());        System.out.println("数量为:"+count);    }}

分组操作

//接收一个 Function 参数groupingBy(Function classifier)
public class CollectDataToArray{    public static void main(String[] args) {        Stream studentStream = Stream.of(                new Student("赵丽颖", 52, 56),                new Student("杨颖", 56, 88),                new Student("迪丽热巴", 56, 99),                new Student("柳岩", 52, 53)        );                //1.按照具体年龄分组        Map> map = studentStream.collect(Collectors.groupingBy((s -> s.getAge())));        map.forEach((key,value)->{            System.out.println(key + "---->"+value);        });        //2.按照分数>=60 分为"及格"一组  <60 分为"不及格"一组        Map> map = studentStream.collect(Collectors.groupingBy(s -> {            if (s.getScore() >= 60) {                return "及格";            } else {                return "不及格";            }        }));        map.forEach((key,value)->{            System.out.println(key + "---->"+value.get());        });        //3.按照年龄分组,规约求每组的最大值(规约:reducing)        Map> reducingMap = studentStream.collect(                Collectors.groupingBy(Student::getAge,                         Collectors.reducing(    BinaryOperator.maxBy(            Comparator.comparingInt(Student::getScore)    )                        )                )        );        reducingMap .forEach((key,value)->{            System.out.println(key + "---->"+value);        });    }}

多级分组操作

//接收两个参数: 1.Function 参数  2.Collector多级分组groupingBy(Function classifier,Collector downstream) 
public class CollectDataToArray{    public static void main(String[] args) {        Stream studentStream = Stream.of(                new Student("赵丽颖", 52, 95),                new Student("杨颖", 56, 88),                new Student("迪丽热巴", 56, 55),                new Student("柳岩", 52, 33)        );        //多级分组        //1.先根据年龄分组,然后再根据成绩分组        //分析:第一个Collectors.groupingBy() 使用的是(年龄+成绩)两个维度分组,所以使用两个参数 groupingBy()方法        //    第二个Collectors.groupingBy() 就是用成绩分组,使用一个参数 groupingBy() 方法        Map>>> map = studentStream.collect(Collectors.groupingBy(str -> str.getAge(), Collectors.groupingBy(str -> str.getScore(), Collectors.groupingBy((student) -> {            if (student.getScore() >= 60) {                return "及格";            } else {                return "不及格";            }        }))));        map.forEach((key,value)->{            System.out.println("年龄:" + key);            value.forEach((k2,v2)->{                System.out.println("\t" + v2);            });        });    }}

分组和分区
分组和分区的区别就在:分组可以有多个组。分区只会有两个区( true 和 false)

//1.一个参数partitioningBy(Predicate predicate)//2.两个参数(多级分区)partitioningBy(Predicate predicate, Collector downstream)
public class CollectDataToArray{    public static void main(String[] args) {        Stream studentStream = Stream.of(                new Student("赵丽颖", 52, 95),                new Student("杨颖", 56, 88),                new Student("迪丽热巴", 56, 55),                new Student("柳岩", 52, 33)        );        //分区操作        Map> partitionMap = studentStream.collect(Collectors.partitioningBy(s -> s.getScore() > 60));        partitionMap.forEach((key,value)->{            System.out.println(key + "---->" + value);        });    }}

拼接操作

//无参数--等价于 joining("");joining()//一个参数joining(CharSequence delimiter)//三个参数(前缀+后缀)joining(CharSequence delimiter, CharSequence prefix,CharSequence suffix)
public class CollectDataToArray{    public static void main(String[] args) {        Stream studentStream = Stream.of(                new Student("赵丽颖", 52, 95),                new Student("杨颖", 56, 88),                new Student("迪丽热巴", 56, 55),                new Student("柳岩", 52, 33)        );                //拼接操作        //无参:join()        String joinStr1 = studentStream.map(s -> s.getName()).collect(Collectors.joining());        System.out.println(joinStr1);        //一个参数:joining(CharSequence delimiter)        String joinStr2 = studentStream.map(s -> s.getName()).collect(Collectors.joining(","));        System.out.println(joinStr2);        //三个参数:joining(CharSequence delimiter, CharSequence prefix,CharSequence suffix)        String joinStr3 = studentStream.map(s -> s.getName()).collect(Collectors.joining("—","^_^",">_<"));        System.out.println(joinStr3);    }}

来源地址:https://blog.csdn.net/yueerba126/article/details/131472361

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯