Stream(流):
什么是stream(流):Stream是一个来自数据源的元素队列并支持聚合操作
元素:是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算
数据源:流的来源。可以是集合,数组,I/O channel, 产生器generator 等
聚合操作:类似SQL语句一样的操作,如:filter, map, reduce, find, match, sorted等
Stream操作的两个基础特征:
Pipelining: 中间操作都会返回流对象本身
这样多个操作可以串联成一个管道,如同流式风格(fluent style)
这样做可以对操作进行优化,比如延迟执行(laziness)和短路( short-circuiting)
内部迭代:
以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代,这叫做外部迭代
Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现
注意:
1. Stream自己不会存储元素
2. Stream不会改变原对象,相反他们会返回一个持有结果的新Stream
3. Stream操作是延迟执行,这意味着他们会等到需要结果的时候才执行
Stream操作步骤:
1.创建流(即初始化流)
2.中间操作:Intermediate
一个流可以后面跟随零个或多个intermediate操作
做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用
常见中间操作:
filter distinct sorted peek limit
skip parallel sequential unordered
map (mapToInt, flatMap 等)
3.终止操作:Terminal
一个流只能有一个terminal操作,执行多个terminal操作会报错:stream has already been operated upon or closed
Terminal 操作的执行,才会真正开始流的遍历并且会生成一个结果或者一个 side effect
常见终止操作:
forEach forEachOrdered toArray reduce collect
min max count anyMatch allMatch noneMatch
findFirst findAny iterator
短路操作:Short-circuiting
对于一个intermediate 操作:
如果它接受的是一个无限大(infinite/unbounded)的Stream,但返回一个有限的新 Stream
对于一个terminal 操作:
如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果
常见短路操作:
anyMatch allMatch noneMatch findFirst findAny limit
流的创建:
方式一:Collection接口相关方法 将集合转换为流
default Stream< E> stream(); //返回一个串行流
default Stream< E> parallelStream(); //返回一个并行流
示例:
List<String> list = Arrays.asList("a","ab","abc","abcd","dh","");
Stream<String> listToStream = list.stream();//串行流
Stream<String> parallelStream = list.parallelStream();//并行流
并行流通过默认的ForkJoinPool,可能提高你的多线程任务的速度。并行流在遍历时可能是无序的
方式二: Arrays类的静态方法 stream() 将数组转换为流
public static <T> Stream<T> stream(T[] array)
示例:
String [] strArray = new String[] {"a", "b", "c","d"};
Stream<String> arrayToStream = Arrays.stream(strArray);
方式三:Stream接口静态方法 of() 初始化流
public static<T> Stream<T> of(T... values)
示例:
Stream<String> stream = Stream.of("a","b","c","d");
方式四:Stream接口静态方法 iterate()、generat()创建无限流
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static<T> Stream<T> generate(Supplier<T> s)
示例:
//0 2
Stream<Integer> iterateStream = Stream.iterate(0, (x) -> x+2).limit(2);
//0.7959655194696649 0.04986900509440961 0.4258300512930294 0.6815133817071739
Stream<Double> generateStrem = Stream.generate(Math::random).limit(4);
流的常用操作:
- 1.筛选/过滤:filter/distinct/limit/skip
方法 | 描述 |
---|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补 |
示例:
Stream<String> strStream = Stream.of("pugb","king","lol","ow","solo","ow","");
strStream
.filter(s -> s.length()>0)//过滤字符串长度小于0的元素
.distinct() //元素去重 (需要重写 hashcode和 equals方法)
.skip(2) //跳过流的前两个元素
.limit(2) //从第三个元素起取2个元素
.forEach(System.out::println);//内部迭代流中的元素
执行结果:lol ow
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
示例:
List<String> list = Arrays.asList("a","ab","abc","abcd","dh");
list.stream()
.map(String::toUpperCase)//将元素转为大写
.forEach(System.out::println);//内部迭代流中的元素
System.out.println("---------------------------------------")
String[] arrayOfWords = {"Hello", "World"};
List<String> words = Arrays.asList(arrayOfWords);
words.stream()
.map(w -> split(""))
.flatMap(Arrays::stream)
.distinct()
.forEach(System.out::println);
执行结果:
A AB ABC ABCD DH
--------------------------------------------------
H
l
o
W
r
d
方法 | 描述 |
---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator comp) | 产生一个新流,其中按比较器顺序排序 |
示例:
List<Integer> sortList = Arrays.asList(1,2,13,4,15,6,17,8,19);
sortList.stream()
.sorted()//排序
.forEach(System.out::println);//内部迭代流中的元素
sortList.stream()
.sorted(Integer::compare)//排序
.forEach(System.out::println);//内部迭代流中的元素
执行结果:1 2 4 6 8 13 15 17 19
方法 | 描述 |
---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了) |
示例:
List<String> matchList = Arrays.asList("pugb","king","lol","ow","solo","ow","");
boolean anyMatch = matchList.stream().anyMatch(str -> str.equals("king"));
boolean noneMatch = matchList.stream().noneMatch(String::isEmpty);
boolean allMatch = matchList.stream().allMatch(String::isEmpty);
System.out.println(anyMatch); //true
System.out.println(noneMatch); //false
System.out.println(allMatch); //false
Optional<String> findFirst = matchList.stream().findFirst();
System.out.println(findFirst.get()); //pugb
Optional<String> findAny = matchList.stream().filter(s -> s.equals("king")).findAny();
System.out.println(findAny.get()); //king
示例:
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax()); //19
System.out.println("列表中最小的数 : " + stats.getMin()); //1
System.out.println("所有数之和 : " + stats.getSum()); //85
System.out.println("平均数 : " + stats.getAverage()); // 9.444444444444445
System.out.println("=========================================================");
OptionalInt max = integers.stream().mapToInt((x) ->x).max();
OptionalInt min = integers.stream().mapToInt((x) ->x).min();
int sum = integers.stream().mapToInt((x) ->x).sum();
OptionalDouble avg = integers.stream().mapToInt((x) ->x).average();
System.out.println("最大值: " +max.getAsInt()); //19
System.out.println("最小值: " +min.getAsInt()); //1
System.out.println(" 和:" +sum); //85
System.out.println("平均值: " +avg.getAsDouble()); // 9.444444444444445
System.out.println("=========================================================");
//Stream中只有max和min两个方法 sum/avg需转换为对应的包装类Stream计算
Optional<Integer> maxInteger = integers.stream().max(Integer::compare);
Optional<Integer> minInteger = integers.stream().min(Integer::compare);
System.out.println(maxInteger.get()); //19
System.out.println(minInteger.get()); //1
方法 | 描述 |
---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。 返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。 返回 Optional< T> |
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它 来进行网络搜索而出名
示例:
//求和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum); //55
准备Employee类:重写hashCode和equals方法
public class Employee {
private Integer id;
private String name;
private Integer age;
private Double salary;
private Integer status;
//省略getter/setter/toString/hashCode/equals方法
}
定义集合:
List<Employee> emps = Arrays.asList(
new Employee(101, "林青霞", 28, 9889.99),
new Employee(102, "东方不败", 29, 4329.85),
new Employee(103, "周星驰", 40, 1233.88),
new Employee(104, "大圣", 500, 5000.44),
new Employee(105, "张无忌", 15, 3000.09),
new Employee(102, "东方不败", 29, 4329.85)
);
收集示例:
emps.stream()
.map(Employee::getName)//将员工姓名转为stream
.collect(Collectors.toList())//将员工姓名stream转为List集合
.forEach(System.out::println);
emps.stream()
.map(Employee::getName)//将员工姓名转为stream
.collect(Collectors.toSet())//将员工姓名stream转为Set集合
.forEach(System.out::println);
emps.stream()
.map(Employee::getName)//将员工姓名转为stream
.collect(Collectors.toCollection(HashSet::new))//将员工姓名stream转为HashSet集合
.forEach(System.out::println);
执行结果:
林青霞 东方不败 周星驰 大圣 张无忌 东方不败
-------------------
周星驰 林青霞 大圣 东方不败 张无忌
-------------------
周星驰 林青霞 大圣 东方不败 张无忌
统计示例:
Long count = emps.stream().collect(Collectors.counting());
System.out.println("员工个数:" + count);
Double avgSalary = emps.stream().
collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println("平均工资:" + avgSalary);
Double sumSalary= emps.stream().
collect(Collectors.summingDouble(Employee::getSalary));
System.out.println("总工资:" + sumSalary);
Optional<Employee> maxSalary = emps.stream()
.collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
System.out.println("最高工资员工:" + maxSalary.get());
Optional<Double> minSalary = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println("最低工资:" + minSalary.get());
//统计分析
DoubleSummaryStatistics doubleSummaryStatistics = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println("平均工资:" + doubleSummaryStatistics.getAverage());
System.out.println("最高工资:" + doubleSummaryStatistics.getMax());
System.out.println("最低工资:" + doubleSummaryStatistics.getMin());
System.out.println("总工资:" + doubleSummaryStatistics.getSum());
//拼接
String join = emps.stream()
.map(Employee::getName)//将员工姓名转为stream
.collect(Collectors.joining(",", "--", "--"));//拼接符"," 前缀"--" 后缀"--"
System.out.println(join);
执行结果:
员工个数:6
平均工资:4630.683333333333
总工资:27784.1
最高工资员工:Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]
最低工资:1233.88
平均工资:4630.683333333333
最高工资:9889.99
最低工资:1233.88
总工资:27784.1
--林青霞,东方不败,周星驰,大圣,张无忌,东方不败--
收集分组:
Map<String, List<Employee>> group = emps.stream()
.collect(Collectors.groupingBy(Employee::getName));
System.out.println(group);
执行结果:
{
周星驰=[Employee [id=103, name=周星驰, age=40, salary=1233.88, status=null]],
林青霞=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]],
大圣=[Employee [id=104, name=大圣, age=500, salary=5000.44, status=null]],
东方不败=[Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null],
Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null]
],
张无忌=[Employee [id=105, name=张无忌, age=15, salary=3000.09, status=null]]}
多级分组:
Map<String, Map<String, List<Employee>>> moreGroup = emps.stream()
.collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy((e) -> {
if (e.getAge() < 30) return "青年";
else if (e.getAge() < 50) return "中年";
else return "老年";
})));
System.out.println(moreGroup);
执行结果:
{
周星驰={中年=[Employee [id=103, name=周星驰, age=40, salary=1233.88, status=null]]},
林青霞={青年=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]]},
大圣={老年=[Employee [id=104, name=大圣, age=500, salary=5000.44, status=null]]},
东方不败={青年=[Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null],
Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null]]
},
张无忌={青年=[Employee [id=105, name=张无忌, age=15, salary=3000.09, status=null]]}}
Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类 供了很多静态 方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
方法 | 返回类型 | 作用 |
---|
toList | List<T> | 把流中元素收集到List |
---|
List<Employee> emps= list.stream().collect(Collectors.toList()); |
---|
toSet | Set<T> | 把流中元素收集到Set |
---|
Set<Employee> emps= list.stream().collect(Collectors.toSet()); |
---|
toCollection | Collection<T> | 把流中元素收集到创建的集合 |
---|
Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new)); |
---|
counting | Long | 计算流中元素的个数 |
---|
long count = list.stream().collect(Collectors.counting()); |
---|
summingInt | Integer | 对流中元素的整数属性求和 |
---|
inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
---|
averagingInt | Double | 计算流中元素Integer属性的平均 值 |
---|
doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
---|
summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值。 如:平均值 |
---|
IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
---|
joining | String | 连接流中每个字符串 |
---|
String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
---|
maxBy | Optional<T> | 根据比较器选择最大值 |
---|
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
---|
minBy | Optional<T> | 根据比较器选择最小值 |
---|
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
---|
reducing | 归约产生的类型 | 从一个作为累加器的初始值 开始,利用BinaryOperator与 流中元素逐个结合,从而归 约成单个值 |
---|
inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
---|
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结 果转换函数 |
---|
inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
---|
groupingBy | Map<K, List<T>> | 根据某属性值对流分组,属 性为K,结果为V |
---|
Map<Emp.Status, List<Emp>> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus)); |
---|
partitioningBy | Map<Boolean,List<T>> | 根据true或false进行分区 |
---|
Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
---|