java8新特性:函数式编程,stream流,Optional 类!
参考文献:
java8 stream流操作的flatMap(流的扁平化)
Lambda语法
-
标准写法
Arrays.sort(arr, (String m, String n) -> Integer.compare(m.length(), n.length()));
lambda表达式的标准写法由下面几点构成:
- 以逗号分隔,以()关闭的形参:
(Dog m, Dog n)
- 箭头标记:
->
- 主体部分则是一个单表达式或者声明代码块。如下是单表达式形式:
Integer.compare(m.getWeight(), n.getWeight())
- 以逗号分隔,以()关闭的形参:
-
参数类型可以推断
如果参数的类型可以根据上下文推断出来,则可以省略掉类型
-
存在多行代码
需要{} 括起来,且代码应该有明确的返回语句
Arrays.sort(arr, (String m, String n) -> { if (m.length() > n.length()) return -1; else return 0; });
-
单个参数并可推断类型
可以省略参数 “x” 的括号
-
方法引用
写成::形式,并省略参数。
stream.forEach(System.out::println);
-
没有参数
() -> {for(int i=0; i<10; i++) doSomthing();}
Optional 类
包含有可选值的包装类,既可以含有对象也可以为空。
常用方法
-
ofNullable()
创建Optional对象:传入的对象即可能是 null 也可能是非 null,不会抛出异常NullPointerException
-
ifPresent()
选择性执行语句:该方法除了执行检查,还接受一个Consumer(消费者) 参数,如果对象不是空的,就对执行传入的 Lambda 表达式:
opt.ifPresent( u -> assertEquals(user.getEmail(), u.getEmail()));
这个例子中,只有 user 用户不为 null 的时候才会执行断言。
-
orElseGet()
返回默认值:这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果:
User result = Optional.ofNullable(user).orElseGet( () -> user2);
orElse() 和 orElseGet() 的不同之处: Optional 对象返回非空值后,orElse() 方法仍然创建了 默认值 对象,而orElseGet() 方法不创建 默认值 对象。
-
orElseThrow()
在对象为空的时候抛出异常,而不是返回备选的值:
@Test(expected = IllegalArgumentException.class) public void whenThrowException_thenOk() { User result = Optional.ofNullable(user) .orElseThrow( () -> new IllegalArgumentException()); }
这个方法让我们有更丰富的语义,可以决定抛出什么样的异常,而不总是抛出 NullPointerException。
-
map()
-
filter()
-
Optional 类的链式方法
String result = Optional.ofNullable(user) .flatMap(User::getAddress) .flatMap(Address::getCountry) .map(Country::getIsocode) .orElse("default");
Stream流
-
概念
Java 8 中的 Stream 是对集合(Collection)对象功能的增强
- 专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation)
- 大批量数据操作 (bulk data operation)
- 支持 Lambda 表达式
- 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的
- 作用更像一个高级版本的 Iterator,与迭代器不同点,Stream 可以并行化操作,迭代器只能命令式地、串行化操作
- Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程
-
"终端操作"&"中间操作"
-
终端操作: 会消费流,这种操作会产生一个结果的
iterator()、 spliterator()、min()、max()、forEach()
-
中间操作:会产生另一个流,类似管道
-
-
缩减操作
缩减操作的三个约束条件
- 无状态
- 不干预
- 关联性
reduce()-通用的方法,而min()和max(),count()这些操作称为特例缩减。
T reduce(T identity, BinaryOperator<T> accumulator);//1 Optional<T> reduce(BinaryOperator<T> accumulator);//2
第一个版本的identity为初始化值(兼容单个值的情况),求和的时候为0,求积的时候它的值为1。
其中的accumulator是一个BinaryOperator
的类型,他是java.util.function包中声明的函数式接口,它扩展了BiFunction函数式接口. @FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T,T,T> { } @FunctionalInterface public interface BiFunction<T, U, R> { R apply(T t, U u);//notice }
apply()对他的两个操作数(t,u)应用到同一个函数上,并返回结果
# 直接函数式编程 reduce((a, b) -> a + b)
-
并行流
对流调用一下parallel(),或者Collection.parallelStream()
在reduce()的第三版本比较适合并行流,accumulator被称为累加器, combiner被称为合成器, combiner定义的函数将accumulator提到的两个值合并起来
public interface Stream<T> extends BaseStream<T, Stream<T>> { //、、、忽略其他无关紧要的元素 <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner); }
-
顺序流&并行流&无序流之间的切换操作
在使用并行流时,有时候流是无序的就能获得性能上的提升,可以通过BaseStream接口提供的unordered()方法把流转换成一个无序流之后,再进行各种操作
forEach()方法不一定会保留并行流的顺序,如果在对并行流的每个元素执行操作时,也希望保留顺序,那么可以使用forEachOrdered()方法,它的用法和forEach()是一样的。
forEach底层采用的是迭代器的方式。如果数据结构是ArrayList这种数据结构,那你可以采用for,但是你的数据结构如果是LinkList那你千万别再用for,应该果断采用forEach,因为数据一多起来的,for此时的效率低得可怜
-
映射
-
filter方法
filter方法的参数是一个Predicate
对象,即一个从T到boolean的函数,返回为true的 -
map方法
对一个流中的值进行某种形式的转换,返回的是对应类型的Stream对象。
-
flatMap方法
flat -- 摊平,流的扁平化
flatMap()操作能把原始流中的元素进行一对多的转换,并且将新生成的元素全都合并到它返回的流里面,即重新整合为一个流。
将多个Stream连接成一个Stream,这时候不是用新值取代Stream的值,与map有所区别,这是重新生成一个Stream对象取而代之。
String[] words = new String[]{"Hello","World"}; List<String> a = Arrays.stream(words) .map(word -> word.split("")) .flatMap(Arrays::stream) .distinct() .collect(toList()); a.forEach(System.out::print); // 输出 ["H","e","l","o","W","r","d"]
-
-
收集功能
-
Collectors
类public static <T> Collector<T, ?, List<T>> toList() public static <T> Collector<T, ?, Set<T>> toSet()
用法:
steam.collect(Collectors.toList)
-
collect方法
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
类似上面的缩减操作。其中supplier指定如何创建用于保存结果的对象,比如,要使用ArrayList作为结果的集合,需要指定它的构造函数,accumulator函数是将一个元素添加到结果中,而combiner函数合并两个部分的结果
private static void learnCollect() { List<HeroPlayerGold> lists = new ArrayList<>(); lists.add(new HeroPlayerGold("盖伦", "RNG-Letme", 100)); lists.add(new HeroPlayerGold("诸葛亮", "RNG-Xiaohu", 300)); lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300)); lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500)); lists.add(new HeroPlayerGold("牛头", "RNG-Ming", 500)); lists.stream().collect(HashSet::new, HashSet::add, HashSet::addAll ).forEach(System.out::println); }
-
-
Spliterator
Spliterator是Java8新增的一种迭代器,Spliterator支持并行迭代。
Stream 常用方法
参考:JDK 8 通过Stream 对List,Map操作和互转
-
list 转 map
List<Student> list = Arrays.asList(new Student(1, 18, "阿龙", GenderColumn.BOY.getCode()), new Student(2, 17, "小花", GenderColumn.GIRL.getCode()), new Student(3, 17, "阿浪", GenderColumn.LADYBOY.getCode())); // value 为对象 student -> student jdk1.8返回当前对象 Map<Integer, Student> map = list.stream().collect(Collectors.toMap(Student::getId, student -> student));
-
map 转 map
Map<String, Map<String, Object>> map1 = aggregationMap.entrySet().stream().collect(Collectors.toMap( e -> e.getKey(), e -> { List<String> options = ((StringTerms) e.getValue()).getBuckets().stream().map(StringTerms.Bucket::getKeyAsString).collect(Collectors.toList()); Map<String, Object> map = new HashMap<>(); map.put("options", options); return map; } ));
-
map 转 list
// stream map 转 list List<Map<String, Object>> paramMapList = aggregationMap.entrySet().stream().map((e) -> { List<String> options = ((StringTerms) e.getValue()).getBuckets().stream().map(StringTerms.Bucket::getKeyAsString).collect(Collectors.toList()); Map<String, Object> map = new HashMap<>(); map.put("k", e.getKey()); map.put("options", options); return map; }).collect(Collectors.toList());