Java 8 in action读书笔记

第一章

为什么关注java 8
  1. 简化代码
    lambda替换匿名内

  2. 更简单的并行思路
    Stream API

  3. Stream可以看作是升级Java 8的主要原因,lambda和默认方法是对Stream的支持。(默认方法是由于JDK为了支持Stream,接口加入新方法,但是以前的代码没有实现了该接口,但是没有实现新增方法,如果没有默认方法,以前的代码将不可用)

  4. 函数式编程是指将代码传递给方法的功能


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有并发优化,并且在满足基石的条件下,基本有免费的并发效果.
函数式编程的思想:

  1. 函数作为值传递
  2. 执行时元素之间无互动
# 顺序处理
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())
# 使用匿名类调用,避免声明过多类
# 54 的区别主要是在调用上
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
# 65 相比调用时候代码更加简单
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表达式
区别

  1. 表达式lambda可以转换为类型Expression的表达式树,而语句lambda不可以
Expression<Func<int, int, int>> expression = (a, b) => a + b;//正确
Expression<Func<int, int, int>> expression1 = (a, b) => { return a + b; };//错误,无法将具有语句体的 lambda 表达式转换为表达式树
  1. 表达式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重新抛出运行时异常
######类型检查,类型推断以及限制

  1. 找到调用的函数的方法签名
  2. 找到传递Lamdba对应的参数类型
  3. 找到其抽象方法
  4. 匹配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的线程,可能会在分配该变量的线程将变量收回之后,去访问变量。所以实际上访问的都是局部变量的副本。(本质上是为了解决内存问题)
###方法引用
方法引用有三类

  1. 静态方法引用Integer::parseInt
  2. 指向任何类型方法的方法引用String::length
  3. 指向现有对象的实例方法的方法引用expensiveTransaction::getValue

2 & 3的区别是

# 2
# 需要的Lambda
(String s) -> s.toUppeCase()
# 引用方法为
String::toUppeCase()
# 3
# 需要的Lambda
() -> expensiveTransaction.getValue()
# 引用方法为
expensiveTransaction::getValue

区别是实例本身是不是Lambda的一个参数,不是就用实例::方法,否则用类::方法
三种方法

构造函数的引用

Class::new根据参数个数赋值给函数式接口,最好调用函数式接口的方法进行实例化


第四章

中间操作与终端操作
中间操作

OperationTypeReturn typeArgument operationFunction descriptor
filterIntermediateStreamPredicateT -> boolean
sortIntermediateStreamFunction<T, R>T -> R
limitIntermediateStream
sortedIntermediateStreamComparator(T, T) -> int
distinctIntermediateStream

终端操作

OperationTypePurpose
forEachTerminalConsumes each element from a stream and applies a lambda to each of them. The operation returns void.
countTerminalReturns the number of elements in a stream. The operation returns a long.
collectTerminalReduces the stream to create a collection such as a List, a Map, or even an Integer. See chapter 6 for more detail.

第五章

谓词
一个返回boolean的函数

筛选 & 切片
  1. filter(Predicate),接受一个谓词,根据谓词返回结果筛选
  2. distinct(),返回无重复元素
  3. limit(n),截断流至前n个
  4. skip(n),跳过前n个元素
映射
  1. map(Function<T, R>),对流中的每一个元素做Function操作,得到一个新的范型类型的流Stream -> Stream
  2. 流的扁平化
    背景
    将字符串列表拆分为字符流
    背景
    方案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 flatMap(Stream)
    flatMap将每个输入的Stream 合并成一个Stream

anyMatch(Predicate), 查找任意一个
allMatch((Predicate)), 全部匹配
noneMatch(Predicate),没有任何匹配
findAny(),返回流中任意元素
findFirst(),返回流中第一个元素
findAny VS findFirst
并行计算时表现不一样,如果不关心顺序尽量用findAny
Optional,主要用于解决null问题。
isPresent(),包含值返回true,否则返回false
ifPresent(Consumer block),值存在则执行代码。
get(),存在则返回值,否则抛NoSuchElement异常
T orElse(T other),值存在则返回值,否则返回默认值
reduce,第一个参数为初始值,第二个参数为BinaryOperator.

数值流

mapToInt() 映射到int省去拆/装箱成本

构建流

Stream.of 由值创建流
Stream.empty() 创建一个空流
Arrays.stream() 由数组创建流
Files.lines(Paths.get(path), Charset.defaultCharset()) 由文件创建流
Stream.iterate() 由函数生成无界流, 接收迭代器
Stream.generate() 由函数生成无界流, 接收函数

汇总与规约
collect()

签名
collect()常见方法
count()求数量,maxBy(Comparator)求最大,summingInt(ToIntFunction)原生int类型的求和,averagingInt(ToIntFunction)原生int类型的求平均数,summarizingInt(ToIntFunction)原声int类型的多维度数据汇总(平均,求和,最大,最小)
joining(delimiter),以特定字符分割
groupingBy分组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值