Java8新特性
1.Lambda表达式与Functional接口
1 | Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) ); |
增加函数式接口的概念。即具有一个方法的普通接口。可以被隐式转换为lambda表达式。增加了一种特殊的注解明确声明接口作为函数式接口的意图:
@FunctionalInterface
,默认方法与静态方法并不影响函数式接口的契约,可以任意使用。
2.接口的默认方法与静态方法
默认方法:默认方法与抽象方法的不同之处在于抽象方法必须要求实现,默认方法则没有这个要求。(如果有需要也可以覆盖这个默认实现)
1 | private interface Defaulable { |
在JVM中,默认方法的实现是非常高效的,并且通过字节码指令为方法调用提供了支持。默认方法允许继续使用现有的Java接口,而同时能够保障正常的编译过程。这方面好的例子是大量的方法被添加到java.util.Collection接口中去:stream(),parallelStream(),forEach(),removeIf(),……
3.方法引用
1 | public static class Car { |
构造器引用
1 | final Car car = Car.create( Car::new ); |
4.Stream
1 | public class Streams { |
1 | final Collection< Task > tasks = Arrays.asList( |
所有状态为OPEN的任务一共有多少分数?
1 | // Calculate total points of all active tasks using sum() |
第一,task集合被转换化为其相应的stream表示。然后,filter操作过滤掉状态为CLOSED的task。下一步,mapToInt操作通过Task::getPoints这种方式调用每个task实例的getPoints方法把Task的stream转化为Integer的stream。最后,用sum函数把所有的分数加起来,得到最终的结果。
.stream操作被分成了中间操作与最终操作这两种。
中间操作返回一个新的stream对象。中间操作总是采用惰性求值方式,运行一个像filter这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生了一个新的stream对象,这个新的stream对象包含原始stream中符合给定谓词的所有元素。像forEach、sum这样的最终操作可能直接遍历stream,产生一个结果或副作用。当最终操作执行结束之后,stream管道被认为已经被消耗了,没有可能再被使用了。在大多数情况下,最终操作都是采用及早求值方式,及早完成底层数据源的遍历。
stream另一个有价值的地方是能够原生支持并行处理。
1 | // Calculate total points of all tasks |
对集合中的元素进行分组。
1 | // Group tasks by their status |
计算整个集合中每个task分数(或权重)的平均值来结束task的例子。
1 | // Calculate the weight of each tasks (as percent of total points) |
[API]
生成流
steam()
:为集合创建串行流。parallelStream()
:危机和创建并行流。
1 | List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); |
forEach
迭代流中的每个数据。
1 | Random random = new Random(); |
map
map 方法用于映射每个元素到对应的结果。
1 | List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); |
filter
filter 方法用于通过设置的条件过滤出元素。
1 | List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); |
limit
limit 方法用于获取指定数量的流。
1 | Random random = new Random(); |
sorted
sorted 方法用于对流进行排序。
1 | Random random = new Random(); |
并行(parallel)程序
parallelStream 是流并行处理程序的代替方法。
1 | List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); |
Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
1 | ist<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); |
统计
1 | List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); |
流的操作类型分为两种:
- Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
- Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
构造流的几种常见方法
1 | // 1. Individual values |
需要注意的是,对于基本数值型,目前有三种对应的包装类型 Stream:
IntStream、LongStream、DoubleStream。当然我们也可以用 Stream、Stream >、Stream ,但是 boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。
流转换为其他数据类型
1 | // 1. Array |
流的操作
1 | 接下来,当把一个数据结构包装成 Stream 后,就要开始对里面的元素进行各类操作了。常见的操作可以归类如下。 |
https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html
Optional
可以优雅的解决NullPointException的问题。
1 | Optional<User> user = …… |
Optional的三种构造方式:
Optional.of(obj)、Optional.ofNullable(obj)和明确的Optional.empty()
假定我们有一个实例 Optional
存在即返回,无则提供默认值
1 | return user.orElse(null); //而不是 return user.isPresent() ? user.get() : null; |
存在即返回,无则由函数来产生
1 | return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase(); |
存在才对它做点什么
1 | user.ifPresent(System.out::println); |
map函数
1 | return user.map(u -> u.getOrders()).orElse(Collections.emptyList()) |
1 | return user.map(u -> u.getUsername()) |
针对这方面 Groovy 提供了一种安全的属性/方法访问操作符 ?.
1 | user?.getUsername()?.toUpperCase(); |
其他几个, filter() 把不符合条件的值变为 empty(), flatMap() 总是与 map() 方法成对的, orElseThrow() 在有值时直接返回, 无值时抛出想要的异常.
使用 Optional 时尽量不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其他像 Optional.orElse(), Optional.orElseGet(), Optional.map() 等这样的方法.
函数式接口
1 |
|
1 | GreetingService greetService1 = message -> System.out.println("Hello " + message); |
提醒:加不加 @FunctionalInterface 对于接口是不是函数式接口没有影响,该注解只是提醒编译器去检查该接口是否仅包含一个抽象方法
eval 函数可以写为如下格式:
1 | private static void eval(List<Integer> list, Predicate<Integer> predicate) { |
或者直接可以不用定义 eval 函数,使用:
1 | list.stream().filter(n -> n > 3).forEach(System.out::println); |