JDK8系列之Stream API入门教程和示例
在前面的章节的学习中,我们学习了jdk8的新特性,lambada表达式、方法引用、函数式接口等等,接着本博客继续JDK8的一个比较重要的特性,JDK8 Stream API
1、什么是Jdk8 Stream?
Stream api是jdk8的新特性,使用jdk中java.util.stream
里库,这种风格将元素集合看作一种stream,stream在管道中传输,在管道节点经过筛选、排序、聚合等操作,然后由terminal操作得到结果
2、Jdk8 Stream优点是什么?
- Java 8 中的 Stream 是对集合(Collection)对象功能的增强,Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码
- 同时Jdk8 Stream api提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势
- Stream stream() : 返回一个顺序流
- Stream parallelStream() : 返回一个并行流
3、Jdk8 Stream的特征
- 不存储数据:Stream数据来源于数据源,但是本身是不存储数据元素的,而是将管道的数据元素传递给操作
- 函数式编程:流的操作不会修改数据
- 延迟操作:流的操作,如Filter,map等中间操作是可以延迟的,只有到terminal操作才会将操作顺序执行
- 可以解绑:stream api有些操作是要求在有限的时间完成的,比如limit(n) 或 findFirst(),这些操作访问到有限的元素后就可以返回
- 一次性消费:流的元素只能访问一次,如果你想重新访问流的元素,你得重新生成一个新的流。
4、Stream流的所有操作函数
- 中间操作(intermediate operation)
- 无状态(Stateless)
- unordered()
- filter()
- map()
- mapToInt()
- mapToLong()
- mapToDouble
- flatMap()
- flatMapToInt()
- flatMapToLong()
- flatMapToDouble()
- peek()
- 有状态(Stateful)
- distinct()
- sorted()
- limit()
- skip()
- 无状态(Stateless)
- terminal 操作(terminal operation)
- 非短路操作
- forEach()
- forEachOrdered()
- toArray()
- reduce()
- collect()
- max()
- min()
- count()
- 短路操作(short-circuiting)
- anyMatch()
- allMatch()
- noneMatch()
- findFirst()
- findAny()
- 非短路操作
5、Stream流的创建方式
- 由集合创建Stream
new ArrayList<>().stream();
- 由数组创建Stream
Arrays.stream(new int[]{1,2,3})
- 由值创建Stream
Stream<Integer> integerStream = Stream.of(1);
- 由文件创建Stream
// example : 从文件读取数据
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("D://javap.txt")));
} catch (FileNotFoundException e) {
// ignore exception
log.error("FileNotFoundException :{}",e);
}
Stream<String> lines = bufferedReader.lines();
lines.forEach(s -> {System.out.println(s);});
读取jar文件
代码语言:javascript复制 Stream<JarEntry> stream = new JarFile("").stream();
6、 Stream有限流和无限流
代码语言:javascript复制// example:创建有限流
IntStream.of(new int[]{1,2,3});
IntStream.range(1,10);
IntStream.rangeClosed(1,10);
//使用Pattern 将字符串分隔成流
Pattern pattern = compile(",");
Stream<String> streams = pattern.splitAsStream("a , b , c , d , e");
streams.forEach( System.out::print);
代码语言:javascript复制 // example :创建无限流
// 无限等比数列
Stream<Integer> columns = Stream.iterate(1 , n -> n*2);
// 生成无限随机数流
Stream<Double> nums = Stream.generate(Math::random);
// 无限数值流
IntStream ints = new Random().ints();
7、intermediate operations
intermediate operations,又被称之为中间操作,中间操作会返回一个新的流,并且操作是延迟执行的,它不会修改原始的数据源,而且是由在终点操作开始的时候才真正开始执行
- distinct 唯一
// example :distinct 唯一
List<String> distinctStrs = Stream.of("a", "b" , "c" , "d" , "e", "b")
.distinct()
.collect(Collectors.toList());
System.out.println(String.format("distinct列表数据:%s" , distinctStrs));
- filter 过滤
// example : filter 过滤
List<Integer> columns = Stream.of(1 ,2 ,-1,3,4,5,6,7,8,9)
.filter(n -> n > 0)
.collect(Collectors.toList());
System.out.println(String.format("filter列表数据:%s" , columns));
- map 映射
// example : map 映射
List<String[]> mapArras = Stream.of("hello","hi")
.map(e -> e.split("") )
.distinct()
.collect(Collectors.toList());
// List<String[]>类型的,不能直接打印
mapArras.forEach(System.out::println);
- filterMap 映射汇总
// example : flatMap 映射汇总
List<String> mapStrs = Stream.of("hello","hi")
.map(e -> e.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
// 通过.flatMap(Arrays::stream)转成string数据
mapStrs.forEach(s->System.out.println(s));
- limit 限制
// example :limit限制
List<Integer> ints = IntStream.range(1,1000).limit(10)
.boxed()
.collect(Collectors.toList());
// 打印[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
System.out.println(ints);
- peek 观察者 peek会提供一个消费函数,每个元素被消费时都会执行这个钩子函数
// example :peek 观察者
Arrays.stream(new String[]{"a","b","c","d","e"})
// 每个元素被消费时都会执行这个钩子
.peek(System.out::println)
.count();
- sorted 排序
//example :sorted 排序
List<Integer> sortedInts = Stream.of(1 ,9, 3, 2, 10,5,8)
.sorted(
(a , b) -> {
return a >b ? 1 :-1;
}
)
//.sorted(Comparator.comparingInt(a -> a))
.collect(Collectors.toList());
System.out.println(sortedInts);
- skip 跳过 skip 跳过丢弃了前n个元素的流,如果流中的元素小于或者等于n,则返回空的流
// example : skip 跳过
List<String> skipStrs = Stream.of("a", "b", "c", "d", "e","f","g","h","i")
//丢弃了前n个元素的流,如果流中的元素小于或者等于n,则返回空的流
.skip(2)
.collect(Collectors.toList());
System.out.println(skipStrs);
8、terminal operations
- match 断言
public boolean allMatch(Predicate<? super T> predicate)
public boolean anyMatch(Predicate<? super T> predicate)
public boolean noneMatch(Predicate<? super T> predicate)
- allMatch只有在所有的元素都满足断言时才返回true,否则flase,流为空时总是返回true
- anyMatch只有在任意一个元素满足断言时就返回true,否则flase,
- noneMatch只有在所有的元素都不满足断言时才返回true,否则flase
- count 计数
// example :count 计数
String[] arr = new String[]{"a","b","c","d" , "e"};
long count = Arrays.stream(arr).count();
System.out.println(count);
- collect 收集
// example : collect 收集
List<String> strs = Stream.of("a", "b" , "c" , "d" , "e", "b")
.collect(Collectors.toList());
System.out.println(strs);
方法 | 返回类型 | 作用 |
---|---|---|
toList() | List<T> | 把流中元素收集到List,List<T> result = list.stream().collect(Collectors.toList()); |
toSet() | Set<T> | 把流中元素收集到Set,Set<T> result = list.stream().collect(Collectors.toSet()); |
toCollection() | Collection<T> | 把流中元素收集到集合,Collection<T> result = lsit.stream().collect(Collectors.toCollection(ArrayListL::new)); |
counting() | Long | 计算流中元素的个数,long count = lsit.stream().collect(Collectors.counting()); |
summingInt() | Integer | 对流中元素的整数属性求和,int total = lsit.stream().collect(Collectors.counting()); |
averagingInt | Double | 计算元素Integer属性的均值,double avg = lsit.stream().collect(Collectors.averagingInt(Student::getAge)); |
summarizingInt | IntSummary | Statistics收集元素Integer属性的统计值,IntSummaryStatistics result = list.stream().collect(Collectors.summarizingInt(Student::getAge)); |
joining | Stream | 连接流中的每个字符串,String str = list.stream().map(Student::getName).collect(Collectors.joining()); |
maxBy | Optional<T> | 根据比较器选择最大值,Opetional<Student> max = list.stream().collect(Collectors.maxBy(comparingInt(Student::getAge))) |
minBy | Optional<T> | 根据比较器选择最小值,Optional<Student> min= list.stream().collect(Collectors.minBy(comparingInt(Student::getAge))); |
reducing | 规约产生的类型 | 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值,int total = list.stream().collect(Collectors.reducing(0, Student::getAge, Integer::sum)); |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果转换,int how = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
groupingBy | Map<K, List<T>> | 根据某属性值对流分组,属性为K,结果为,Map<Integer, List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getStatus)); |
partitioningBy | Map<Boolean, List<T>> | 根据true或false进行分区,Map<Boolean, List<Student>> map = list.stream().collect(Collectors.partitioningBy(Student::getPass)); |
- find 返回
- findAny()返回任意一个元素,如果流为空,返回空的Optional,对于并行流来说,它只需要返回任意一个元素即可
- findFirst()返回第一个元素,如果流为空,返回空的Optional。
- forEach、forEachOrdered 遍历 forEach遍历流的每一个元素,对于有序流按照它的encounter order顺序执行,你可以使用forEachOrdered方法
Stream.of(1,2,3,4,5).forEach(System.out::println);
- min、max
- max返回流中的最大值,
- min返回流中的最小值。
// example :max、min
long minV = Stream.of(1,2,3,4,5).max(
new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
).get();
long maxV = Stream.of(1,2,3,4,5).max(
new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}
).get();
System.out.println(String.format("min value :%s , max value :%s", minV ,maxV));
- reduce 归约 reduce是一个变成操作,依照某种运算规则依次执行,所以reduce可以用于做sum,min,max,average等等操作
方法 | 描述 |
---|---|
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值,返回 Optional |
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值,返回 T |
reduce(U identity, BiFunction a, BinaryOperator combiner) | 可以将流中元素反复结合起来,得到一个值,返回 Optional |
// example : reduce
// reduce用于求和
List<Integer> sumInts = Stream.of(1 ,2,3,4,5,6,7,8,9).collect(Collectors.toList());
Optional<Integer> sum = sumInts.stream().reduce(Integer::sum);
System.out.println(String.format("reduce计算的总值:%s" , sum));
- toArray
// example : toArray
Integer[] integers = Stream.of(1 ,2,3,4,5,6,7,8,9).toArray(Integer[]::new);
System.out.println(integers);
- concat 组合
// example : concat 组合
List<String> list1 = Stream.of("a","b","c").collect(Collectors.toList());;
List<String> list2 = Stream.of("d","e","f").collect(Collectors.toList());;
Stream.concat(list1.stream() , list2.stream()).forEach(System.out::println);
9、Stream灵活应用例子
- 数据去重 数据去重,除了前面的distinct,也可以用TreeSet来做去重,总之,stream非常灵活,开发者可以根据自己理解,进行编程
handleModels.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(HandleModel::getUserCode))), ArrayList::new)
);
- 数据筛选
List<ControlVo> timeoutControlList = list.stream().filter(e-> e.getDays()!=null && e.getDays() < 0 ).sorted(Comparator.comparing(ControlVo::getDays)).collect(Collectors.toList());
- PO、VO、DTO的转换
// PO转DTO
dtoList = handleModels.stream().map(e -> {
HandleDto dto = new HandleDto ();
BeanUtil.copyProperties(e, dto);
return dto;
}).collect(Collectors.toList());
- 和Optional一起使用 list有数据时候,才进行遍历
Optional.ofNullable(list).ifPresent(
hlist -> {
hlist.stream().forEach((e) -> {
});
});
- 分挑存储 什么是分挑存储?就是根据集合实体类的一个字段,进行分组然后重新组合成新的集合
ps:按照StuffId进行分组
代码语言:javascript复制 Map<String, List<AttachmentDto>> attachmentGroup =
attachmentDtos.stream()
.filter(item -> StringUtils.isNotBlank(item.getStuffId()))
.collect(Collectors.groupingBy(AttachmentDto::getStuffId));
10、附录参考资料
- https://www.runoob.com/java/java8-new-features.html
- https://beginnersbook.com/2017/10/java-8-features-with-examples/
- https://howtodoinjava.com/java-8-tutorial/