一. 前言
随着Java
的发展,越来越多的企业开始使用JDK1.8
版本。JDK1.8
是自 JDK1.5
之后最重要的版本,这个版本包含语言、编译器、库、工具、JVM等方面的十多个新特性。本次文章将着重学习Stream
。
Stream
是JDK1.8
中处理集合的关键抽象概念,Lambda
和 Stream
是JDK1.8
新增的函数式编程最有亮点的特性了,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API
对集合数据进行操作,就类似于使用SQL
执行的数据库查询。Stream
使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
简而言之,Stream API
提供了一种高效且易于使用的处理数据的方式。
二. 什么是Stream
1.Stream(流)是一个来自数据源的元素队列并支持聚合操作。
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
2.和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
特点:
- Stream 不是数据结构,不会保存数据。
- Stream 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(保留意见:毕竟peek方法可以修改流中元素)
- 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
三. 关于Stream API
1. Stream API分类
Stream 操作分为中间操作或者终止操作两种,终止操作返回一特定类型的计算结果,而中间操作返回Stream本身,Stream 的操作类型具体分类如下:
解释:
-
无状态:指元素的处理不受之前元素的影响;
-
有状态:指该操作只有拿到所有元素之后才能继续下去。
-
非短路操作:指必须处理所有元素才能得到最终结果;
-
短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。
2. 如何使用Stream流?
使用Stream流分为三步。
-
创建Stream
一个数据源(如:集合、数组),获取一个Stream流。
-
中间操作
一个中间操作链,对数据源(如:集合、数组)的数据进行处理。
-
终止操作
一个终止操作,执行中间操作链,并产生一个计算结果。
3. Stream的中间操作和结束操作
- 中间操作
filter: 过滤流,过滤流中的元素,返回一个符合条件的Stream
map: 转换流,将一种类型的流转换为另外一种流。(mapToInt、mapToLong、mapToDouble 返回int、long、double基本类型对应的Stream)
flatMap:简单的说,就是一个或多个流合并成一个新流。(flatMapToInt、flatMapToLong、flatMapToDouble 返回对应的IntStream、LongStream、DoubleStream流。)
distinct: 返回去重的Stream。
sorted: 返回一个排序的Stream。
peek: 主要用来查看流中元素的数据状态。
limit: 返回前n个元素数据组成的Stream。属于短路操作
skip: 返回第n个元素后面数据组成的Stream。
- 结束操作
forEach: 循环操作Stream中数据。
toArray: 返回流中元素对应的数组对象。
reduce: 聚合操作,用来做统计。
collect: 聚合操作,封装目标数据。
min、max、count: 聚合操作,最小值,最大值,总数量。
anyMatch: 短路操作,有一个符合条件返回true。
allMatch: 所有数据都符合条件返回true。
noneMatch: 所有数据都不符合条件返回true。
findFirst: 短路操作,获取第一个元素。
findAny: 短路操作,获取任一元素。
forEachOrdered: 暗元素顺序执行循环操作。
四. 如何获取Stream流
在 Java 8 中, 集合接口有两个方法来生成流:
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流。
1. 常见几种集合流的创建
/** * stream,获取各种集合的stream流 */
@Test
public void testCollectionStream(){
//List集合
List<String> stringList = new ArrayList<>();
//Set集合
Set<String> stringSet = new HashSet<>();
//Map集合
Map<String,Object> stringObjectMap = new HashMap<>();
//数组
String[] stringArray = {"张三三","李四","王五","王五","赵八",};
//通过list获取stream流
Stream<String> streamList = stringList.stream();
//通过set获取stream流
Stream<String> streamSet = stringSet.stream();
//通过map获取stream流
Stream<String> streamMap = stringObjectMap.keySet().stream();
//通过array获取stream流
Stream<String> streamArray1 = Stream.of(stringArray);
}
2. 构造流的几种常见方法
@Test
public void testCollectionStream(){
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String[] strArray = new String[]{"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
}
五. Stream在代码中的使用方式
关于Stream的常见操作方式,主要分为两大类: 中间操作和终止操作 ,接下来就通过这两大分类,讲解下具体的语法用法。
1. 流的中间操作
1.1 筛选过滤
- filter:过滤流中的某些元素
/** * filter 方法 , 返回符合过滤条件的值 */ @Test public void testFilter() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.stream().filter(e -> e.contains("张")).forEach(System.out::println); }
或
- filter多个过滤筛选条件
/** * list集合stream流式操作 */ @Test public void testStreamList() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.stream().filter(e -> e.startsWith("张")) //过滤所有姓张的人 .filter(e -> e.length() == 3) //过滤所有姓名是3个字的人 .forEach(System.out::println); //遍历打印,System.out::println表明System.out调用println打印方法 }
limit(n):获取前n个元素
/** * limit 方法 ,返回前n个元素数据值组成的Stream。 */ @Test public void testLimit() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); list.stream().limit(3).forEach(System.out::println); //取前3个 }
-
skip(n):跳过n元素,配合limit(n)可实现分页
/** * skip方法 ,跳过前n个元素的中间流操作,返回剩下的值。 */ @Test public void testSkip() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); //list.stream().skip(3).forEach(System.out::println); //跳过前3个 list.stream().skip(3).limit(2).forEach(System.out::println); //skip+limit实现分页 }
-
distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素
/** * distinct, 返回去重的Stream */ @Test public void testDistinct() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("李四"); list.add("王五"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); list.stream().distinct().collect(Collectors.toList()).forEach(System.out::println); }
1.2 排序
-
sorted():自然排序,流中元素需实现Comparable接口
/** * sorted: 返回一个排序的Stream */ @Test public void testSorted() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("李四"); list.add("王五"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); list.stream().distinct().sorted().collect(Collectors.toList()).forEach(System.out::println); }
-
sorted(Comparator com):定制排序,自定义Comparator排序器
1.3 映射
- map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
/** * 遍历map集合,截取substring(2)开始的值 */
@Test
public void testMap() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
Stream<String> stream = list.stream().map(e -> e.substring(2));
stream.forEach(System.out::println);
}
-
forEach:ForEach流式遍历集合
/** * forEach, ForEach流式遍历list集合 */ @Test public void testForEach() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.stream().forEach(System.out::println); }
2. 流的终止操作
2.1 匹配、聚合操作
-
allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
/** * allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false */ @Test public void testAllMatch() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("李四"); list.add("王五"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); boolean b = list.stream() .allMatch(e -> list.size() > 8); System.out.println("b = " + b); }
-
noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
/** * noneMatch: 接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false */ @Test public void testNoneMatch() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("李四"); list.add("王五"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); boolean b = list.stream().noneMatch(e->e.equals("张三")); System.out.println("b = " + b); }
-
anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
/** * anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false */ @Test public void testAnyMatch() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("李四"); list.add("王五"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); boolean b = list.stream().anyMatch(e -> e.equals("王二麻子")); System.out.println("b = " + b); }
-
findFirst:返回流中第一个元素
/** * findFirst:返回流中第一个元素 */ @Test public void testFindFirsth() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("李四"); list.add("王五"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); Optional<String> first = list.stream().findFirst(); System.out.println("first = " + first.get()); }
-
findAny:返回流中的任意元素
/** * findAny:返回流中第一个元素 */ @Test public void testFindAny() { List<String> list = new ArrayList<>(); list.add("张三三"); list.add("李四"); list.add("李四"); list.add("王五"); list.add("王五"); list.add("孙七"); list.add("赵八"); list.add("王二麻子"); Optional<String> any = list.stream().findAny(); System.out.println("any = " + any.get()); }
-
count:返回流中元素的总个数
/** * count,获取List集合的长度 */ @Test public void testCount() { List<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 = " + count); int size = list.size(); System.out.println("size = " + size); }
-
max:返回流中元素最大值
/** * max:返回流中元素最大值 */ @Test public void testMax() { List<Integer> list = new ArrayList<>(); list.add(11); list.add(22); list.add(33); list.add(44); list.add(55); list.add(66); list.add(77); list.add(88); Integer integer = list.stream().max(Integer::compareTo).get(); System.out.println("integer = " + integer); }
-
min:返回流中元素最小值
/** * min:返回流中元素最小值 */ @Test public void testMin() { List<Integer> list = new ArrayList<>(); list.add(11); list.add(22); list.add(33); list.add(44); list.add(55); list.add(66); list.add(77); list.add(88); Integer integer = list.stream().min(Integer::compareTo).get(); System.out.println("integer = " + integer); list.stream().limit(1).limit(2).distinct().skip(3).filter(f -> f.equals(55)).forEach(System.out::println); }
2.2 Collector 工具库:Collectors
-
Collectors
Student s1 = new Student("aa", 10,1); Student s2 = new Student("bb", 20,2); Student s3 = new Student("cc", 10,3); List<Student> list = Arrays.asList(s1, s2, s3); //装成list List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList()); // [10, 20, 10] //转成set Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet()); // [20, 10] //转成map,注:key不能相同,否则报错 Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge)); // {cc=10, bb=20, aa=10} //字符串分隔符连接 String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc) //聚合操作 //1.学生总数 Long count = list.stream().collect(Collectors.counting()); // 3 //2.最大年龄 (最小的minBy同理) Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get(); // 20 //3.所有人的年龄 Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge)); // 40 //4.平均年龄 Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge)); // 13.333333333333334 // 带上以上所有方法 DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge)); System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage()); //分组 Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge)); //多重分组,先根据类型分再根据年龄分 Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge))); //分区 //分成两部分,一部分大于10岁,一部分小于等于10岁 Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10)); //规约 Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get(); //40
六、Stream操作代码
为了方便小伙伴们看到这篇博客时,学习的更加轻松,这里贴出源码,小伙伴们学习是可贴到IDEA运行查看Stream过滤筛选的结果,以此对Stream的流式操作更加熟悉。
package com.java8.example.chapter3;
import org.junit.jupiter.api.Test;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** * @desc: Stream流式操作 * @author: cao_wencao * @date: 2020-09-17 15:24 */
public class TestStreamList {
/** * list集合stream流式操作 */
@Test
public void testStreamList() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.stream().filter(e -> e.startsWith("张")) //过滤所有姓张的人
.filter(e -> e.length() == 3) //过滤所有姓名是3个字的人
.forEach(System.out::println); //遍历打印,System.out::println表明System.out调用println打印方法
}
/** * stream,获取各种集合的stream流 */
@Test
public void testCollectionStream() {
List<String> stringList = new ArrayList<>();
Set<String> stringSet = new HashSet<>();
Map<String, Object> stringObjectMap = new HashMap<>();
String[] stringArray = {"张三三", "李四", "王五", "王五", "赵八",};
//通过list获取stream流
Stream<String> streamList = stringList.stream();
//通过set获取stream流
Stream<String> streamSet = stringSet.stream();
//通过map获取stream流
Stream<String> streamMap = stringObjectMap.keySet().stream();
//通过array获取stream流
Stream<String> streamArray1 = Stream.of(stringArray);
}
/** * forEach, ForEach流式遍历list集合 */
@Test
public void testForEach() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.stream().forEach(System.out::println);
}
/** * filter 方法 , 返回符合过滤条件的值 */
@Test
public void testFilter() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.stream().filter(e -> e.contains("张")).forEach(System.out::println);
}
/** * 遍历map集合,截取substring(2)开始的值 */
@Test
public void testMap() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
Stream<String> stream = list.stream().map(e -> e.substring(2));
stream.forEach(System.out::println);
}
/** * count,获取List集合的长度 */
@Test
public void testCount() {
List<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 = " + count);
int size = list.size();
System.out.println("size = " + size);
}
/** * limit 方法 ,返回前n个元素数据值组成的Stream。 */
@Test
public void testLimit() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
list.stream().limit(3).forEach(System.out::println); //取前3个
}
/** * skip方法 ,跳过前n个元素的中间流操作,返回剩下的值。 */
@Test
public void testSkip() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
//list.stream().skip(3).forEach(System.out::println); //跳过前3个
list.stream().skip(3).limit(2).forEach(System.out::println); //skip+limit实现分页
}
/** * collect,将流转化为集合 */
@Test
public void testCollect() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
List<String> collect = list.stream().skip(3).limit(2).collect(Collectors.toList());
collect.forEach(System.out::println);
}
/** * distinct, 返回去重的Stream */
@Test
public void testDistinct() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("李四");
list.add("王五");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
list.stream().distinct().collect(Collectors.toList()).forEach(System.out::println);
}
/** * sorted: 返回一个排序的Stream */
@Test
public void testSorted() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("李四");
list.add("王五");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
list.stream().distinct().sorted().collect(Collectors.toList()).forEach(System.out::println);
}
/** * anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false */
@Test
public void testAnyMatch() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("李四");
list.add("王五");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
boolean b = list.stream().anyMatch(e -> e.equals("王二麻子"));
System.out.println("b = " + b);
}
/** * noneMatch: 接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false */
@Test
public void testNoneMatch() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("李四");
list.add("王五");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
boolean b = list.stream().noneMatch(e->e.equals("张三"));
System.out.println("b = " + b);
}
/** * allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false */
@Test
public void testAllMatch() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("李四");
list.add("王五");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
boolean b = list.stream()
.allMatch(e -> list.size() > 8);
System.out.println("b = " + b);
}
/** * findFirst:返回流中第一个元素 */
@Test
public void testFindFirsth() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("李四");
list.add("王五");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
Optional<String> first = list.stream().findFirst();
System.out.println("first = " + first.get());
}
/** * findAny:返回流中第一个元素 */
@Test
public void testFindAny() {
List<String> list = new ArrayList<>();
list.add("张三三");
list.add("李四");
list.add("李四");
list.add("王五");
list.add("王五");
list.add("孙七");
list.add("赵八");
list.add("王二麻子");
Optional<String> any = list.stream().findAny();
System.out.println("any = " + any.get());
}
/** * max:返回流中元素最大值 */
@Test
public void testMax() {
List<Integer> list = new ArrayList<>();
list.add(11);
list.add(22);
list.add(33);
list.add(44);
list.add(55);
list.add(66);
list.add(77);
list.add(88);
Integer integer = list.stream().max(Integer::compareTo).get();
System.out.println("integer = " + integer);
}
/** * min:返回流中元素最小值 */
@Test
public void testMin() {
List<Integer> list = new ArrayList<>();
list.add(11);
list.add(22);
list.add(33);
list.add(44);
list.add(55);
list.add(66);
list.add(77);
list.add(88);
Integer integer = list.stream().min(Integer::compareTo).get();
System.out.println("integer = " + integer);
list.stream().limit(1).limit(2).distinct().skip(3).filter(f -> f.equals(55)).forEach(System.out::println);
}
}
总结
以上就是对于JDK1.8中Stream流式计算的一个简单介绍,关于JDK1.8中的新特性,用法远远不止这些,这篇文章只作为一个简单的入门使用,更深层次的理解请后续关注JDK1.8系列的文章。
长按下图二维码,关注公众号「Thinking曹」,在通往Java架构的路上我想与你一同前行,共同进步!
今天的文章JDK1.8新特性Stream流式操作分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17758.html