JDK8 管道 Stream 详细使用介绍
Stream 的获取
① 通过集合Collection获取
List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
Stream<Integer> stream = list.stream();
② 通过数组获取
String[] array = {"are","you","ok"};
Stream<String> stream = Arrays.stream(array);
//对于基本类型数组的处理
int[] array = {1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(array).boxed();
//Arrays.stream(array)获取的是一个IntStream对象,boxed 方法用于将目前 Stream 中的基本类型装箱
③ 直接通过值获取
Stream<String> stream = Stream.of("are","you","ok");
Stream 常用管道操作
1) 筛选 filter
filter函数接收一个Lambda表达式作为参数,该表达式返回 boolean,在执行过程中,流将元素逐一输送给filter,并筛选出执行结果为 true 的元素;
//筛选出列表中的非空项
List<String> list = Arrays.asList("are","you","","ok");
List<String> filted = list.stream()
.filter(x-> !x.empty())
.collect(Collectors.toList());
2) 去重 distinct
//去除列表中的重复元素
List<String> list = Arrays.asList("are","you","you","ok");
List<String> distincted = list.stream()
.distinct()
.collect(Collectors.toList());
3) 截取 limit
截取流的前N个元素:
//获取Stream的前3个值
List<String> list = Arrays.asList("are","you","fucking","ok");
List<String> distincted = list.stream()
.limit(3);
.collect(Collectors.toList());
4) 跳过 skip
跳过流的前n个元素:
List<String> list = Arrays.asList("are","you","fucking","ok");
List<String> distincted = list.stream()
.skip(2);
.collect(Collectors.toList());
5) 映射 map
对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。流会将每一个元素输送给map函数,并执行map中的Lambda表达式,最后将执行结果存入一个新的流中。
如:将 list 中每一个 Integer类型元素自增后转化为 String类型
//将集合中的每一个元素+1,并转为字符串
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<String> result = list.stream()
.map(x->String.valueOf(++x))
.collect(Collectors.toList());
//统计集合中>3的元素数量
int result = list.stream().filter(x -> x > 3).count();
6) 合并多个流 flatMap
List<String> list1 = .....
List<String> list2 = ...
List<List<String>> list = Arrays.asList(list1,list2);
//将list中的list1,list2合并为一个List<String>
List<String> listsum = list.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
以下一个实际的应用例子:列出 list 中各不相同的单词;
List<String> list = new ArrayList<String>();
list.add("I am a boy");
list.add("I love the girl");
list.add("But the girl loves another girl");
list.stream().map(line->line.split(" ")) //将每一个项分词,并映射为数组
.flatMap(Arrays::stream) //将每一个分项数组组合并到主流中,形成一个包含所有分项数组的总数组流
.distinct() //去重
.forEach(System.out::println); //打印
7)匹配元素
①是否匹配任一元素:anyMatch
anyMatch用于判断流中是否存在至少一个元素满足指定的条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。
//判断流中是否含有>10的项
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
boolean result = list.stream()
.anyMatch(x->x>10);
② 是否匹配所有元素:allMatch
allMatch用于判断流中的所有元素是否都满足指定条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。
//判断流中是否全部>5
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
boolean result = list.stream()
.allMatch(x->x>5);
③ 是否未匹配所有元素:noneMatch
noneMatch与allMatch恰恰相反,它用于判断流中的所有元素是否都不满足指定条件:
//判断流中是否 全部不满足 >5
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
boolean result = list.stream()
.noneMatch(x->x>5);
8)获取元素
①获取任一元素 :findAny
findAny从流中随机选出 一个元素出来,它返回一个Optional类型的元素。
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
Optional<Integer> result = list.stream().findAny();
if(result.isPresent())
Integer randValue = result.get();
//or:
Integer randValue = result.orElse(0);
//合并的调用方式
Integer randValue = list.stream().findAny().orElse(0);
② 获取第一个元素:findFirst
Optional<Integer> result = list.stream().findFirst();
Optional 对象介绍
Optional是Java8新加入的一个容器,这个容器只存1个或0个元素,它用于防止出现NullpointException,它提供如下方法:
- isPresent()
判断容器中是否有值。 - ifPresent(Consume lambda)
容器若不为空则执行括号中的Lambda表达式。 - T get()
获取容器中的元素,若容器为空则抛出NoSuchElement异常。 - T orElse(T other)
获取容器中的元素,若容器为空则返回括号中的默认值。
9) 归约统计
归约是将集合中的所有元素经过指定运算,折叠成一个元素输出,如:求最值、平均数等,这些操作都是将一个集合的元素折叠成一个元素输出;
//获取一个整型列表的最大值,最小值
Random random = new Random();
List<Integer> list = random.ints(1,100).limit(50).boxed().collect(Collectors.toCollection(ArrayList::new));
int max = list.stream().max(Integer::compare).orElse(-1); //获取最大值
int min = list.stream().min(Integer::compare).orElse(-1); //获取最小值
//获取一个Bean列表的最大值对象,最小值对象,使用自定义的 Comparator
List<Bean> beans = new ArrayList<>();
for(int i = 0; i < 50; i++){
beans.add(new Bean(String.valueOf(random.nextInt(10000)),random.nextInt(20)));
}
Bean maxBean = beans.stream().max(Comparator.comparingInt(Bean::getOrderNum)).get();
Bean mixBean = beans.stream().min((bean1, bean2) ->{ //使用自定义的 lambda 表达式
if(bean1.getId() == null){
bean1.setOrderNum(0);
}
if(bean2.getId() == null){
bean2.setOrderNum(0);
}
return Integer.compare(bean1.getOrderNum(), bean2.getOrderNum());
}).get();
//使用基于数据流的方式,将流装载相应的 SummaryStatistics 来进行归约计算,可以实现更多的操作;
IntSummaryStatistics stats = list.stream().mapToInt(x -> x). summaryStatistics();
int max = stats.getMax()); //获取最大值
int min = stats.getMin()); //获取最小值
double sum = stats.getSum()); //获取总值
double avg = stats.getAverage()); //获取平均值
long count = stats.getCount(); //获取总数量
10) 遍历流 forEach
数据流提供了新的forEach方法遍历该流中的每个元素,方法参数为一个Lambda表达式,用于对每一个遍历的元素执行的操作;
//输出10个随机数
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
11) 排序 sorted
sorted方法用来流排序,默认升序排布,可以使用一个 Comparator 作为参数来实现自定义排序;
//输出10个排序好的随机数
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println); //升序
random.ints().limit(10).sorted(x1,x2 -> Integer.compare(x2, x1)).forEach(System.out::println); //降序
12)parallelStream 并行处理
parallelStream是流 进行并行处理的替代方案,parallelStream 的底层实现为 ForkJoin 线程池,JDK8 为为 parallelStream 提供了一个通用的线程池,对于代码实际运行时的 parallelStream 线程数量是不可控的,但是可以通过设置 JVM 运行参数 -Djava.util.concurrent.ForkJoinPool.common.parallelism=N (N为线程数量)来调整 JVM ForkJoinPool 的线程数量;
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//get count of empty string
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
数值流的使用
采用Stream 的 redure 方法 进行数值归约操作会涉及到基本数值类型和引用数值类型之间的装箱、拆箱操作,因此效率较低;
此时最好是将当流操作为纯数值操作时,这样使用数值流能获得较高的效率;
StreamAPI提供了三种数值流:IntStream、DoubleStream、LongStream;
1)将 Stream 转换成数值流
StreamAPI 提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong(参数:相应的转化Lambda表达式);
将数值流转化为Stream 的方法:boxed;
List<Double> list = Arrays.asList(2.3,2.4,2.5,2.7,2.8);
//普通Stream转为数值Stream
DoubleStream doubles = list.stream().mapToDouble(x->x); //转化为DoubleStream
IntStream ints = list.stream().mapToInt(x->Integer.parseInt(String.format("%.0f",x))); //转化为IntStream,同时进行取舍操作
//数值Stream转为普通Stream
Stream stream = list.boxed();
2)数值流的数值计算
每种数值流都提供了数值计算函数,如max、min、sum、avg等。
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
OptionalInt max = list.stream().max();
int max_value = max.orElse(0);
OptionalInt sum = list.steam().sum();
iny sum_value = sum.orElse(0);
由于数值流可能为空,并且给空的数值流计算最大值是没有意义的,因此max函数返回OptionalInt,它是Optional的一个子类,能够判断流是否为空,并对流为空的情况作相应的处理。
此外,mapToInt、mapToDouble、mapToLong进行数值操作后的返回结果分别为:OptionalInt、OptionalDouble、OptionalLong
Stream 转换为 Collection、Array、String
Stream 可以通过 Collector 收集器,将其转化为 Array,Collection,Map,String;
① Stream -> 数组
//普通转换
Stream<String> stream = Stream.of("are","you","ok");
String[] array = stream().toArray();
//涉及拆箱、装箱操作的转换
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
int[] array = stream.mapToInt(x->x).toArray();
Integer[] array = stream.toArray(Integer[]::new);
//将 List<Inetegr> 转化为 String[]
List<Integer> list = Arrays.asList(1,2,3,4,5);
String[] array = list.stream().map(String::valueOf).toArray(String[]::new);
② Stream -> List
List<Integer> list1 = stream.collect(Collectors.toList());
List<Integer> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
③ Stream ->Set
Set<Integer> set = stream.collect(Collectors.toCollection(Set::new));
④ Stream ->Stack
Stack<Integer> stack = stream.collect(Collectors.toCollection(Stack::new));
⑤ Stream ->Map
Map<Integer, String> map = Stream.of("are","you","ok").collect(Collectors.toMap( s -> s.hashCode(), s -> s));
⑥ Stream -> String
Stream 可以很方便转化为 String,利用这一个特性,可以十分方便地将一个 Collection(List,Set等)转化为使用某个标点符号分隔的字符串;
//将 List 转化为使用 “,” 分隔的字符串
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
String str = list.stream().map(x->x.toString()).collect(Collectors.joining(","));
System.out.println(str);
//输出: 1,2,3,4,5,6,7
对 Map 使用Stream
虽然 JDK8 的 Stream API 不直接支持 Map,但是我们可以通过对 Map 的 entrySet,keySet,valueColletion 生成 Stream 来进行曲线救国,如下示例:
Map<Integer,String> map = new HashMap<Integer,String>(){{
put(1,"are");
put(2,"you");
put(3,"ok"); }};
map.entrySet().forEach(System.out::println); //遍历key-value
int randomKey = map.keySet().stream().findAny().orElse(-1); //随机取出一个key
String values = map.values().stream().distinct().collect(Collectors.joining(",")); //将value去重后组装成使用“,”分隔的字符串