一、java8 新特性
java8 和java7 相比迎来了许多新特性,包括stream流、函数式编程、lambda表达式等等,在实际的工作中存在大量的stream流使用场景,因此用本文来记录一下在工作中常见的stream流的使用。
二、 stream 流简介
java8 stream流不同于传统的InputStream和OutPutStream。它专注于对集合对象进行各种便利、高效的聚合操作或大批量数据操作。stream结合lambda表达式以提高程序效率和可读性,提供串行、并行两种模式进行数据汇聚操作,其中并发模式为开发者省略了复杂的并发编码流程,让用户可以很方便的写出高性能的并发程序。摘自 《java8中的stream API详解》
三、stream常见方法
为啥要使用stream?很大的原因是它相较于传统的for循环具有更快的处理速度,更优雅的处理方式,更高效而又简单的并发编程支持.
很多时候想到stream流都是因为需要对一个集合中的元素做一系列操作,例如过滤特定元素、分类、取每个元素的某属性数据、求和、排序等等,简单来说就是实现一个filter-map-reduce的过程,产生一个最终结果或者一个副作用。
(一)、stream&Collection&Array&Map 转化
构造stream
// 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 转List: collect(Collectors.toList()) stream.collect(Collectors.toList()); // stream 转Set: collect(Collectors.toSet()) stream.collect(Collectors.toSet()); // stream 转Map:将Person的name属性作为key,将Person本身作为value,如果两个key重复了,使用新的key) Person p1 = new Person("p1",23); Person p2 = new Person("p2",21); Person p3 = new Person("p3",25); stream.collect(Collectors.toMap(Person::getName,Function.Identity,(c1,c2)->c2));
(二)、map/flatMap
map/flatMap实现了将输入元素映射成为输出元素
- map:针对一个元素进行映射
List<Integer> nums = Arrays.asList(1, 2, 3, 4); List<Integer> squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());
- flatMap:针对1对多关系将一个元素映射为多个元素
List<Integer> nums = Arrays.asList(1, 2, 3, 4); List<Integer> squareNums = nums.stream(). map(n -> n * n). collect(Collectors.toList());
(三)、filter
filter实现对stream中的元素的过滤,不满足判断条件的元素将会被过滤,最终得到一个新的stream
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(4);
list.add(14);
list.add(56);
list.add(12);
// 过滤大于3的所有元素,得到一个新的结果
List<Integer> filterResult = list.stream().filter(num -> num > 3).collect(Collectors.toList());
(四)、forEach
stream流的forEach操作和普通的javafor循环在效率上并没有区别,唯一的区别在于stream流接收了一个lambada表达式,它是函数式编程和java语言结合的一个产物。
notice:
- 使用forEach时不能对集合内的数据进行修改,否则会抛出ConcurrentModificationException 异常。不能使用return或者
- forEach结束之后,stream流将会被终止,不能再被执行stream的其他操作
(五)、FindFirst
当需要从stream流中拿到第一个数据的时候,findFirst会为你提供一个安全有效的方式:Optional
// persons中拿到最小年龄的对象 ,若persons为空,则返回一个空的person persons.stream().sorted(Comparator.comparing(Person::getAge)).findFirst().orElse(new Person());
- findFirst 返回stream流中的第一个对象的Optional容器(Optional可以优雅的对空对象的处理)
- Optional提供的orElse方法会在Option包装的对象为空时提供一个默认的对象。
(六)、max/min/distinct/sorted
如果希望对stream中的数据取最大最小值、去重、按照自然数排序等操作可以直接调用上述的方法
// 获取optional类型的最大最小值(可能list为空,因此返回optional类型,避免直接) Optional<Integer> max = list.stream().max(Comparator.comparing(Integer::intValue)); Optional<Integer> min = list.stream().min(Comparator.comparing(Integer::intValue)); // 通过distinct去重,另一种方式是直接获取一个Set:········list.stream.collect(Collectors.toSet()); List<Integer> distinctList = list.stream().distinct().collect(Collectors.toList()); // 使用sorted实现排序,最佳效率为O(NlogN) List<Integer> sorted = list.stream().sorted().collect(Collectors.toList());
(七)、match
使用3种match方法实现对stream流中的数据进行快速的过滤,只要有一个数据不满足就直接退出,返回false
- allMatch:Stream 中全部元素符合传入的 predicate,返回 true
- anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
- noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true