明天开始看,哈哈哈哈哈
第一章
为什么关注java 8
-
简化代码
lambda替换匿名内 -
更简单的并行思路
Stream API -
Stream可以看作是升级Java 8的主要原因,lambda和默认方法是对Stream的支持。(默认方法是由于JDK为了支持Stream,接口加入新方法,但是以前的代码没有实现了该接口,但是没有实现新增方法,如果没有默认方法,以前的代码将不可用)
-
函数式编程是指将代码传递给方法的功能
1.1.2 流处理
特性:流处理是并行的
基石(限制):没有共享的可变数据(不支持可变的数据,虽然可以通过synchronized关键字使用可变数据,但是无法使用java 8带来的一系列关于流的优化),以及将代码传递给其他方法的能力
ps. synchronized慢是因为触发了内核的,多处理器缓存一致性协议
1.1.3 用行为参数化把代码传递给方法
以排序为例,介绍了,在排序时,可以把不同的排序方法代码传递给方法
1.2 JAVA 中的函数
JAVA 8 新增了值的新形式--函数,可以传递给方法。
File[] hiddenFiles = new File(".").listFiles(File::isHidden)
由于为了只使用一两次的小方法,创建方法代码块会增加代码的混乱程度,所以出现了Lambda(匿名方法)
例子
# Before JAVA 8
## compare with color
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = Lists.newArrayList();
for(Apple apple : inventory) {
if ("green".equals(apple.getColor())) {
result.add(apple);
}
}
}
## compare with weight
public static List<Apple> filterHeavyApples(List<Apple> inventory) {
List<Apple> result = Lists.newArrayList();
for(Apple apple : inventory) {
if (apple.getWeight() > 150) {
result.add(apple);
}
}
}
# After JAVA 8
## 定义
public static boolean isGreenApple(Apple apple) {
return "green".equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
return apple.getWeight() > 150;
}
public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = Lists.newArrayList();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
## 调用
filterApples(inventory, Apple::isGreenApple);
filterApples(inventory, Apple::isHeavyApple);
# Lambda
## 定义
public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = Lists.newArrayList();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
## 调用
filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );
filterApples(inventory, (Apple a) -> a.getWeight() > 150 );
ps.使用Lambda还是函数,要以代码的整洁易懂为准绳。
1.3 流
Stream API对于多核CPU有并发优化,并且在满足基石的条件下,基本有免费的并发效果.
函数式编程的思想:
- 函数作为值传递
- 执行时元素之间无互动
# 顺序处理
List<Apple> heavyApples =
inventory.stream().filter((Apple a) -> a.getWeight() > 150)
.collect(toList());
# 并行处理
List<Apple> heavyApples =
inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
.collect(toList());
1.4 默认方法
多个接口有相同方法的默认实现时,通过子类必须自己实现解决菱形继承问题。
1.5 其他好思想
模式匹配,还没看懂,后面补充
// TODO
第二章
通过七种方法,展示了代码如何应对需求变化
# 绿苹果
# 1
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = Lists.newArrayList();
for(Apple apple : inventory) {
if ("green".equals(apple.getColor())) {
result.add(apple);
}
}
}
# 各种颜色苹果,颜色单一维度
# 2
public static List<Apple> filterColorApples(List<Apple> inventory, String color) {
List<Apple> result = Lists.newArrayList();
for(Apple apple : inventory) {
if (color.equals(apple.getColor())) {
result.add(apple);
}
}
}
# 颜色,重量两个维度,再增加维度,还要修改代码
# 3
public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
List<Apple> result = Lists.newArrayList();
for (Apple apple : inventory) {
if ( (flag && color.equals(apple.getColor()))
|| (!flag && apple.getWeight() > weight) ) {
result.add(apple);
}
}
}
# 根据抽象条件筛选,每种不同的筛选行为,实现ApplePredicate接口
# 4
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = Lists.newArrayList();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
}
public class AppGreenColorPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}
filterApples(inventory, new AppGreenColorPredicate())
# 使用匿名类调用,避免声明过多类
# 5 与 4 的区别主要是在调用上
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = Lists.newArrayList();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
}
filterApples(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
});
# Lambda
# 6 与 5 相比调用时候代码更加简单
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = Lists.newArrayList();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
}
filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
# 范型方法, Predicate<T>范型接口为JAVA 8 内置接口
# 7
public static List<T> filterApples(List<T> inventory, Predicate<T> p) {
List<T> result = Lists.newArrayList();
for (T t : inventory) {
if (p.test(t)) {
result.add(t);
}
}
}
第三章
基本语法:
(parameters) -> expression //表达式lambda
(parameters) -> { statements; } //语句lambda
Ps.
(String s) -> { "IronMan"; } 不是Lambda表达式
区别
- 表达式lambda可以转换为类型Expression<T>的表达式树,而语句lambda不可以
Expression<Func<int, int, int>> expression = (a, b) => a + b;//正确
Expression<Func<int, int, int>> expression1 = (a, b) => { return a + b; };//错误,无法将具有语句体的 lambda 表达式转换为表达式树
- 表达式lambda 可以兼容void类型
#以下两个表达式都对
Predicate<String> p = s -> list.add(s);
Consumer<String> c = s -> list.add(s);
哪里可以使用Lambda:
函数式接口可以使用。函数式接口指的是,只有一个抽象方法的接口。可以用@FunctionalInterface标注,此注解主要用于编译期检查。
函数描述符
函数式接口的抽象方法的签名基本上就是Lambda表达式的签名
Runnable 接口可以表示为() -> void,表示列表为空,返回为void的函数
要注意Lambda的函数描述符和接口一致
Lambda实践,环绕执行模式
背景
不同任务周围都围绕着相同的代码。比如处理文件的时候,先要初始化,然后再处理任务,最后要清理。
实践
public static String processFile() throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return br.readLine();
}
}
String result = processFile((BufferedReader br) -> br.readLine() + br.readLine());
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException
}
public static String processFile(BufferedReaderProcessor p) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return p.process(br);
}
}
函数式接口的使用
常用接口
Predicate: T -> boolean
Function: T-R
Consumer: T->void
原始类型参数化
主要用于避免拆箱,适当提高性能
装箱后,需要额外的内存,且需要内存搜索来获取原始值
JDK提供了一些接口
接口名的格式为:
原始类型To原始类型Function,IntPredicate int -> boolean,等,可以搜索下
异常处理
Lambda不允许抛出任何受检查的异常,可以通过try...cache重新抛出运行时异常
类型检查,类型推断以及限制
- 找到调用的函数的方法签名
- 找到传递Lamdba对应的参数类型
- 找到其抽象方法
- 匹配Lambda的函数描述符和抽象方法的参数列表
相同的Lambda,不同的函数式接口
Lambda表达式和函数式接口的匹配方式为参数列表匹配,只要参数列表相同,同一个Lambda表达式可以匹配多个函数式接口。
void类型兼容
表达式lambda 可以兼容void类型
#以下两个表达式都对
Predicate<String> p = s -> list.add(s);
Consumer<String> c = s -> list.add(s);
使用局部变量
lambda可以使用局部变量,但是局部变量必须显示或者事实上final的,即声明为final或者只赋值过一次
局部变量限制的原因
实例变量在堆中,局部变量在栈中。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将变量收回之后,去访问变量。所以实际上访问的都是局部变量的副本。(本质上是为了解决内存问题)
方法引用
方法引用有三类
- 静态方法引用Integer::parseInt
- 指向任何类型方法的方法引用String::length
- 指向现有对象的实例方法的方法引用expensiveTransaction::getValue
2 & 3的区别是
# 2
# 需要的Lambda
(String s) -> s.toUppeCase()
# 引用方法为
String::toUppeCase()
# 3
# 需要的Lambda
() -> expensiveTransaction.getValue()
# 引用方法为
expensiveTransaction::getValue
区别是实例本身是不是Lambda的一个参数,不是就用实例::方法,否则用类::方法
三种方法
构造函数的引用
Class::new根据参数个数赋值给函数式接口,最好调用函数式接口的方法进行实例化
第四章
中间操作与终端操作
中间操作
Operation | Type | Return type | Argument operation | Function descriptor |
---|---|---|---|---|
filter | Intermediate | Stream<T> | Predicate<T> | T -> boolean |
sort | Intermediate | Stream<R> | Function<T, R> | T -> R |
limit | Intermediate | Stream<T> | ||
sorted | Intermediate | Stream<T> | Comparator<T> | (T, T) -> int |
distinct | Intermediate | Stream<T> |
终端操作
Operation | Type | Purpose |
---|---|---|
forEach | Terminal | Consumes each element from a stream and applies a lambda to each of them. The operation returns void. |
count | Terminal | Returns the number of elements in a stream. The operation returns a long. |
collect | Terminal | Reduces the stream to create a collection such as a List, a Map, or even an Integer. See chapter 6 for more detail. |
第五章
谓词
一个返回boolean的函数
筛选 & 切片
- filter(Predicate<T>),接受一个谓词,根据谓词返回结果筛选
- distinct(),返回无重复元素
- limit(n),截断流至前n个
- skip(n),跳过前n个元素
映射
- map(Function<T, R>),对流中的每一个元素做Function操作,得到一个新的范型类型的流Stream<T> -> Stream<R>
- 流的扁平化
背景
将字符串列表拆分为字符流
背景
方案1
该方法将会得到一个Stream<String[]>因为word -> word.split("") 对每个单词返回一个String[]
Ps.word -> word.split("") 不可写为String::split("")。
String::split("")的函数描述符是(String a, String b) -> a.split("b")不符合Function<T, R> 的 T -> R
所以,后续的distinct和collect都是对Stream<String[]>的操作。
方案
Stream<String> flatMap(Stream<String>)
flatMap将每个输入的Stream<String> 合并成一个Stream<String>
anyMatch(Predicate<T>), 查找任意一个
allMatch((Predicate<T>)), 全部匹配
noneMatch(Predicate<T>),没有任何匹配
findAny(),返回流中任意元素
findFirst(),返回流中第一个元素
findAny VS findFirst
并行计算时表现不一样,如果不关心顺序尽量用findAny
Optional<T>,主要用于解决null问题。
isPresent(),包含值返回true,否则返回false
ifPresent(Consumer<T> block),值存在则执行代码。
get(),存在则返回值,否则抛NoSuchElement异常
T orElse(T other),值存在则返回值,否则返回默认值
reduce,第一个参数为初始值,第二个参数为BinaryOperator<T>.
数值流
mapToInt() 映射到int省去拆/装箱成本
构建流
Stream.of 由值创建流
Stream.empty() 创建一个空流
Arrays.stream() 由数组创建流
Files.lines(Paths.get(path), Charset.defaultCharset()) 由文件创建流
Stream.iterate() 由函数生成无界流, 接收迭代器
Stream.generate() 由函数生成无界流, 接收函数
网友评论