Java8 新特性——Stream流
创始人
2024-03-27 02:10:26
0

Java 8中提供了一个新的附加程序包,称为java.util.stream。该软件包由类,接口和枚举组成,以允许对元素进行功能样式的操作。

可以使用流来过滤,收集,打印以及从一种数据结构转换为另一种数据结构等。

流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

 什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

Stream提供以下功能:

  • 流不存储元素。它只是通过计算操作的流水线从数据结构,数组或I/O通道等源中传递元素。
  • 对流执行的操作不会修改原来数据源。例如,对从集合中获取的流进行过滤会产生一个新的不带过滤元素的流,而不是从源集合中删除元素。
  • Stream是惰性的,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
  • 在流的生存期内,流的元素只能访问一次。像Iterator一样,必须生成新的流以重新访问源中的相同元素。

生成流

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流,由主线程按顺序对流执行操作。

  • parallelStream() − 为集合创建并行流,内部以多线程并行执行的方式对流进行操作。

除了直接创建并行流,还可以通过 parallel()把顺序流转换成并行流

创建Stream

1、通过Collection系列集合提供的stream()或parallelStream()

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

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

Integer[] nums = new Integer[]{1,2,3,4};
Stream Integerstream = Arrays.stream(nums);

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

Stream TestStream = Stream.of(1,2,3,4);
//iterate()、generate()方法可以创建无限流,可以通过limit()方法来限制数量。
Stream TestStream2 = Stream.iterate(0,x->x+2);
Stream TestStream3 = Stream.generate(()-> new Random().nextInt(10));
Stream TestStream4 = Stream.iterate(0,x->x+2).limit(10);

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

BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream lineStream = reader.lines();
//上面可以写成一行
Stream lineStream2 = new BufferedReader(new FileReader("F:\\test_stream.txt")).lines();

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

Pattern pattern = Pattern.compile(",");
Stream stringStream2 = pattern.splitAsStream("a,b,c,d");

6、数值范围生成流

// 生成0到10的int流
IntStream intStream = IntStream.rangeClosed(0, 10);
// 生成0到9的int流
IntStream intStream1 = IntStream.range(0, 10);

Stream流操作类型

中间操作(Intermediate):可以有多个,每次返回一个新的流,可进行链式操作,其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用,仅仅调用到这类方法,并没有真正开始流的遍历。

终端操作(Terminal):只能有一个,每次执行完,这个流也就用光光了,无法执行下一个操作,因此只能放在最后,Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。

Stream操作分类
中间操作无状态filter map flatMap peek指元素的处理不受之前元素的影响
有状态distinct sorted limit skip指该操作只有拿到所有元素之后才能继续下去
结束操作非短路操作forEach reduce collect max min count指必须处理所有元素才能得到最终结果
短路操作anyMatch allMatch noneMatch findFirst findAny指遇到某些符合条件的元素就可以得到最终结果

中间操作

filter:返回一个通过设置的条件过滤出元素的流

distinct:   通过流中元素的 hashCode() 和 equals() 去除重复元素

limit(n):   返回一个不超过给定长度的流。

skip(n):    返回一个跳过前n个元素的流,可配合limit(n)可实现分页。

sorted :   返回正序排序后的流

// 过滤所有空字符串,注意是返回 true的留下
Stream.of("1", null, "2", "", "3").filter(StringUtils::isNotEmpty).forEach(System.out::print);// 去掉重复的2
Stream.of("1", "2", "2", "2", "3").distinct().forEach(System.out::print);// 跳过前两个元素
Stream.of("1", "2", "3", "4", "5").skip(2).forEach(System.out::print);// 与skip相反,只留下前2个
Stream.of("1", "2", "3", "4", "5").limit(2).forEach(System.out::print);//排序
List strings = Arrays.asList(10, 2, 30, 22, 1,0, -9);
List sorted = strings.stream().sorted().collect(Collectors.toList());

map:  接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

flatMap:  接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

List list = Arrays.asList("a,b,c", "1,2,3");//将每个元素转成一个新的且不带逗号的元素Stream s1 = list.stream().map(s -> s.replaceAll(",", ""));s1.forEach(System.out::println); // abc  123Stream s3 = list.stream().flatMap(s -> {//将每个元素转换成一个streamString[] split = s.split(",");Stream s2 = Arrays.stream(split);return s2;});s3.forEach(System.out::println); // a b c 1 2 3

终止操作

匹配、聚合操作

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值

List list = Arrays.asList(1, 2, 3, 4, 5);  boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4); //true Integer findFirst = list.stream().findFirst().get(); //1
Integer findAny = list.stream().findAny().get(); //1 long count = list.stream().count(); //5
Integer max = list.stream().max(Integer::compareTo).get(); //5
Integer min = list.stream().min(Integer::compareTo).get(); //1 

规约操作

T reduce(T identity, BinaryOperator accumulator) :可以将流中的元素反复结合起来,得到一个值。返回T。
Optional reduce(BinaryOperator accumulator) :可以将流中的元素反复结合起来,得到一个值。返回Optional。

//计算1-10的自然数的和
List list= Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
//计算公司员工总工资
Stream doubleStream = emps.stream().map(Employee::getSalary);
Optional sumMoney = doubleStream.reduce(Double::sum);
System.out.println(sumMoney);

collect    收集器,将流转换为其他形式。

List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl");
Set set = strings.stream().collect(Collectors.toSet());
List list = strings.stream().collect(Collectors.toList());
//v.concat("_name"):键,v1 -> v1:值
Map map = strings.stream().collect(Collectors.toMap(v ->v.concat("_name"), v1 -> v1, (v1, v2) -> v1));

 

 

Collector 是一个接口,有以下5个抽象方法:

Supplier < A>supplier():创建一个结果容器A
BiConsumer accumulator():消费型接口,第一个参数为容器A,第二个参数为流中元素T。
BinaryOperator< A> combiner():函数接口,该参数的作用跟上一个方法(reduce)中的combiner参数一样,将并行流中各 个子进程的运行结果(accumulator函数操作后的容器A)进行合并。
Function finisher():函数式接口,参数为:容器A,返回类型为:collect方法最终想要的结果R。
Set characteristics():返回一个不可变的Set集合,用来表明该Collector的特征。有以下三个特征:
CONCURRENT:表示此收集器支持并发。
UNORDERED:表示该收集操作不会保留流中元素原有的顺序。
IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。

Person p1 = new Person("zhangsan",26);
Person p2 = new Person("lisi",22);
Person p3 = new Person("wangwu",23);
List list = Arrays.asList(p1,p2,p3);
//装成list
List ageList = list.stream().map(Person::getAge).collect(Collectors.toList());//[26,22,22]
//转成set
Set ageSet = list.stream().map(Person::getAge).collect(Collectors.toSet());//[26,22]//转成map,注:key不能相同,否则报错
Map studentMap = list.stream().collect(Collectors.toMap(Person::getName, Person::getAge)); 
// {zhangsan=26, lisi=22, wangwu=22}//字符串分隔符连接
String joinName = list.stream().map(Person::getName).collect(Collectors.joining(",", "(", ")")); 
// (zhangsan,lisi,wangwu)//聚合操作
//1.总数
Long count = list.stream().collect(Collectors.counting()); // 3
//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compare)).get(); // 26
//3.所有人的年龄求和
Integer sumAge = list.stream().collect(Collectors.summingInt(Person::getAge)); // 70
//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Person::getAge)); // 23.333333333333332
// 带上以上所有方法
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Person::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());//分组 按年龄分组
Map> ageMap = list.stream().collect(Collectors.groupingBy(Person::getAge));
//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
//规约
Integer allAge = list.stream().map(Person::getAge).collect(Collectors.reducing(Integer::sum)).get(); //40 

相关内容

热门资讯

清朝有很多有名的历史人物,能给... 清朝有很多有名的历史人物,能给大家说几个清朝的历史人物吗?林则徐,张之洞,乾隆皇帝,曾国藩,朱耷,这...
曹丕的“太子四友”指的是谁 曹丕的“太子四友”指的是谁首先说,曹丕这四个,陈群司马懿是顶级的谋士和政治家,吴质有些小聪明,朱铄不...
在这次遇难者中存在了几名幸存者... 在这次遇难者中存在了几名幸存者。这句话是不是逻辑错误这句话的逻辑没有问题,有问题的是用词不当。遇难者...
在中国古代,有许多充满哲学智慧... 在中国古代,有许多充满哲学智慧的成语典故、寓言故事,如...在中国古代,有许多充满哲学智慧的成语典故...
清澈的意思是什么,… 清澈的意思是什么,…清净而明澈清而透明【造句】看着他清澈而又天真的眼眸,我的心久久不能平静……
蚂蚁森林合种爱情树一方退出怎么... 蚂蚁森林合种爱情树一方退出怎么找回来两个人合种的爱情树,我退出来,我怎么再次加入进去继续合作那个树?...
有好看的古代修炼小说推荐吗? 有好看的古代修炼小说推荐吗?古代重生穿越修炼......让我帮你找一下这些古代修炼的小说,找到这些类...
女主重生爱上前世辜负的人 女主重生爱上前世辜负的人重生我是你正妻渣女重生之竹马重生之弃渣重生之夫君可欺重生之换我疼你重生妇归来...
华胥引有广播剧吗 华胥引有广播剧吗现有的华胥引的广播剧是忆语广播剧社出品的,只有十三月和杯(这个是错字,请无视,居然输...
选文韩麦尔先生在说了,我的朋友... 选文韩麦尔先生在说了,我的朋友们我就要离开你们呢了,再见了银头鲑鱼tj75rt6yturdrruv ...
中通快递从北京保定市到广东揭阳... 中通快递从北京保定市到广东揭阳普宁要多久?中通快递从北京保定市到广东揭阳普宁要多久?从北京到广东需要...
关于离婚悲伤的歌曲 关于离婚悲伤的歌曲关于离婚悲伤的歌曲林俊杰《可惜没如果》 张靓颖《我走以后》 金志文《流着泪说分手》...
形容文笔差怎么说啊? 形容文笔差怎么说啊?哥哥姐姐,麻烦问下,我是做文员的,形容文案方面的工作很差应该怎么说啊?粗鄙怎么样...
我想做未婚妈妈,可行吗? 我想做未婚妈妈,可行吗?没关系吧?我同学好多他们妈妈都是30岁以后省得他们,都没事啊,但是如果你自己...
如何在两个excel表格里筛选... 如何在两个excel表格里筛选出重复的名字如何在两个excel表格里筛选出重复的名字1、电脑打开EX...
且试天下 哪些小说是用白绫做武... 且试天下 哪些小说是用白绫做武器的?神雕侠侣吖- -..小龙女一开始就是用白绫的聊斋 辛十四娘嘿嘿~...
自带高冷体质,笑起来温暖又治愈... 自带高冷体质,笑起来温暖又治愈的星座,你了解吗?虽然天生高冷体质,但是笑起来特别的温暖治愈的新作用天...
火星未解之谜 火星未解之谜多列举一些,每个事例最好长一点,谢啦~“火星人脸”, “地表被水冲击河道”,“原始大气和...
我是1991年10月4号生的,... 我是1991年10月4号生的,是什么星座啊有的说是处女座有的说是天平座,糊涂了,到底是什么啊很负责任...
梦见白狐狸,然后当时我骑着自行... 梦见白狐狸,然后当时我骑着自行车,我想躲开它,他很凶的的追赶我,最后它向我扑了过来,然后我就醒乐.你...