JDK9下载
https://www.oracle.com/java/technologies/javase/javase9-archive-downloads.html
第一部分 基础知识
第1章 Java8 9 10 以及11变化
//筛选隐藏文件final File[] files = new File(".").listFiles(new FileFilter() {@Overridepublic boolean accept(File file) {return file.isHidden();}});//筛选隐藏文件 java8写法new File(".").listFiles(File::isHidden);
在Java8里写下File::isHidden的时候,你就创建了一个方法引用,同样可以传递它。
什么是谓词 谓词在数学上常常用来代表类似于函数的东西,它接受一个参数值,并返回true,或false。
第2章 通过行为参数化传递代码
2.2 行为化参数

2.4真实的例子
行为化参数是一个很有用的模式,它能够轻松地适应不断变化的需求。这种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创造的行为将方法的行为参数化。
第3章 lambda表达式
3.1 Lambda 管中窥豹
可以把lambda表达式理解为一种简洁的可传递匿名函数:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个抛出的异常列表。
第二部分 使用流进行函数式数据处理
第4章 引入流
4.1 流是什么
流,允许你以声明性方式处理数据集合。

因为filter、sorted、map 和collect等操作是与线程模型无关的高层次构件,所以他们的内部实现时单线程的,也可能透明地充分利用你的多核架构!在实践中,这意味着你用不着为了让某些数据处理任务并行而去操心线程和锁,StreamApi都替你做好了。
public class Dish {private final String name;private final boolean vegetarian;private final int calories;private final Type type;public Dish(String name, boolean vegetarian, int calories, Type type) {this.name = name;this.vegetarian = vegetarian;this.calories = calories;this.type = type;}public String getName() {return name;}public boolean isVegetarian() {return vegetarian;}public int getCalories() {return calories;}public Type getType() {return type;}@Overridepublic String toString() {return name;}public enum Type {MEAT, FISH, OTHER}}List<Dish> menu = Arrays.asList(new Dish("pork", false, 800, Dish.Type.MEAT),new Dish("beef", false, 700, Dish.Type.MEAT),new Dish("chicken", false, 400, Dish.Type.MEAT),new Dish("french fries", true, 530, Dish.Type.OTHER),new Dish("rice", true, 350, Dish.Type.OTHER),new Dish("season fruit", true, 120, Dish.Type.OTHER),new Dish("pizza", true, 550, Dish.Type.OTHER),new Dish("prawns", false, 300, Dish.Type.FISH),new Dish("salmon", false, 450, Dish.Type.FISH));
4.2 流简介
流:从支持数据处理操作的源生成的元素序列。
- 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素。但流的目的在于表达计算。集合讲的是数据,流讲的是计算。
- 源——流会使用一个提供数据的源。比如集合、数组或I/O资源。从有序集合生成流时会保存原有的顺序,由列表生成的流,其元素顺序与列表一致。
- 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,比如filter map reduce find mathc sort等。流操作可以顺序执行,也可以并行执行。
- 流水线——很多流操作本身会返回一个流。这样多个操作就可以链接起来,构成一个更大的流水线。
- 内部迭代——与集合使用迭代器进行显示迭代不同,流的迭代操作是在后台进行的。
final List<String> threeHighCaloricDishNames = menu.stream()//.filter(dish -> dish.getCalories() > 300)//.map(Dish::getName)//获取菜名.limit(3)//.collect(Collectors.toList());System.out.println(threeHighCaloricDishNames);
- filter——接受一个lambda,从流中排除某些元素。
- map——接受一个lambda,将元素转换成其他形式或提取信息。
- limit——截断流,使其元素不超过给定数量。
- collect——将流转换成其他形式。
4.3.2 外部迭代与内部迭代
使用Collection接口需要用户去做迭代(for-each),这称为外部迭代。相反,Stream库使用内部迭代——它帮你把迭代做了。还把得到的流存在了某个地方,你只需要给出一个函数说要干什么就可以了。
4.4 流操作

4.4.1 中间操作
诸如filter或sorted等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理——他们很懒。
4.4.2 终端操作
终端操作会从流的流水线生成结果,其结果是任何不适流的值,比如List Integer,甚至void。
4.4.3 使用流
总而言之,流的使用一般包括三件事
- 一个数据源 来执行一个查询
- 一个中间操作链,形成一条流的流水线
- 一个终端操作,执行流水线,并能生成结果。
第5章 使用流
5.1 筛选
5.1.1 用谓词筛选

5.2 流的切片
通过其他方式选择或跳过流中的某些元素。使用Stream的一些操作结合谓词,你可以高效地选择或者丢弃流中的元素,或者按照设定的大小对流实施截断操作。
5.2.1 使用谓词对流进行切片
Java9 引入两个新方法,可以高效地选择流中的元素,这两个方法分别是:takeWhile和dropWhile。
- takeWhile
```java
List
specialMenu = Arrays.asList( new Dish("seasonal fruit", true, 120, Dish.Type.OTHER),new Dish("prawns", false, 300, Dish.Type.FISH),new Dish("rice", true, 350, Dish.Type.OTHER),new Dish("chicken", false, 400, Dish.Type.MEAT),new Dish("french fries", true, 530, Dish.Type.OTHER));
//java8|方式1
final List
采用方式1 初始列表中的元素已经按照热量进行了排序操作!这里采用filter的缺点是,你需要遍历整个流中的数据,对其中每一个元素执行谓词操作。**而你本来可以在发现第一个热量大于320卡路里的菜肴就停止处理的。**如果你要处理的列表规模不大,这不算什么问题,但是,如果你要处理的一个由海量元素构成的流,采用恰当的方式锁带来的恶性能提升还是很乐观的。takeWhile操作就是为此而生的!它可以帮助你利用谓词对流进行分片(即便你要处理的流是无限流也毫不困难),更秒的是,它会在遭遇第一个不符合要求的元素时就停止处理
2. dropWhile
如果你想要的是其他元素,又该如何。譬如,你想要的的是找出那些热量大于320卡路里的元素。借助dropWhile操作可以达到这一目标。
<a name="AtlaD"></a>
#### 5.2.2 截短流
<a name="nGYpr"></a>
#### 5.2.3 跳过流
skip
<a name="ZzpOy"></a>
### 5.3 映射
一个非常常见的数据处理套路就是从某些对象中选择信息,比如在SQL中,你可以从表中选择一列。Stram API 也通过map和flatMap方法提供了类似的工具。
<a name="FIITk"></a>
#### 5.3.1 对流中的每一个元素应用函数
流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用**映射**一词,是因为它和**转换**类似,但其中到的细微差别在于它是“创建”一个新版本,而不是去“修改”)。
<a name="XvnaL"></a>
#### 5.3.2 流的扁平化
给定单词列表["Hello","World"],你想返回列表 ["H","e","l", "o","W","r","d"]
```java
words.stream()
.map(word -> word.split(""))
.distinct()
.collect(toList());
这个方法的问题在于,传递给map方法的lambda 为每个单词返回了一个 String[] ( String)。因此map返回的流实际上是 Stream
String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);
final List<String> collect = streamOfwords.map(word -> word.split(""))//将每个单词转换成由其字母构成的数组
.flatMap(Arrays::stream)//将各个生成的扁平化 为单个流
.distinct().collect(Collectors.toList());

一言以蔽之,flatMap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。
5.4 查找和匹配
常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性,Stream API通过allMatch,anyMatch,noneMatch findFirst findAny方法提供了这样的工具。
5.4.1 检查谓词是否至少匹配一个元素
5.4.3 查找元素
findAny方法将返回当前流中任意元素。它可以与其他流操作结合使用。
final Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian)
.findAny();
5.5 归约
把一个流中的元素组合起来,使用reduce操作来表达更复杂的查询。此类查询需要将流中的所有元素反复结合起来,得到一个值。这样的查询可以被归类为规约操作。用函数式编程的术语来说,这称为折叠。
5.5.1 元素求和
public class Demo01 {
static Trader raoul = new Trader("Raoul", "Cambridge");
static Trader mario = new Trader("Mario", "Milan");
static Trader alan = new Trader("Alan", "Cambridge");
static Trader brian = new Trader("Brian", "Cambridge");
static List<Transaction> transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
public static void main(String[] args) {
//找出2011年发生的所有交易,并按交易额排序(从低到高)
final List<Transaction> tr2011 = transactions.stream()//
.filter(transaction -> transaction.getYear() == 2011)//
.sorted(Comparator.comparing(Transaction::getValue))
.collect(Collectors.toList());
//交易员都在哪些不同的城市工作过
final List<String> cities = transactions.stream().map(transaction -> transaction.getTrader().getCity()).distinct().collect(Collectors.toList());
//查找所有的剑桥的交易员,并按姓名排序
final List<Trader> traders = transactions.stream().map(Transaction::getTrader)
.filter(trader -> trader.getCity().equals("Cambridge")).collect(Collectors.toList());
//返回所有交易员的姓名字符串,按照字母顺序排序|次方法效率不高,使用下面的更合适
final String reduce = transactions.stream().map(transaction -> transaction.getTrader().getName())
.distinct().sorted().reduce(" ", (n1, n2) -> n1 + n2);
final String traderStr = transactions.stream().map(transaction -> transaction.getTrader().getName())
.distinct().sorted().collect(Collectors.joining());
}
}
5.7 数值流
final Integer reduce = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
这段代码的问题是,暗含一个装箱成本,每个Integer都必须被拆箱成一个原始类型,在进行求和。
5.7.1 原始类型流特化
Java8引入了三个原始类型特化流接口来解决这个问题:IntStream、DoubleStream和LongStream,分别将流中的元特化为int,long,double,从而避免了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum。此外还有在必要时再把他们转换回对象流的方法。要记住的是,这些特化的原因并不在于流的复杂性,而是装箱造成的复杂性——即类似int和Integer之间的效率差异。
- 映射到数值流
将流转成成特化版本的常用方法是mapToInt mapToDouble mapToLong 这些方法和前面的map方法的工作方式一样,只是它们返回的是一个特化流,而不是Stream
final int sum = menu.stream()
.mapToInt(Dish::getCalories).sum();
上述求和,如果流是空的,sum则默认返回0,IntStream还支持其他的方便方法,如max min average等。
- 转换为对象流
同样,一旦有了数值流,你可能会想把它转换回非特化流。例如IntStream上的操作只能产生原始整数:IntStream的map操作接受的Lambda必须接受int并返回int。
要把原始流转换成一般流(每个int都会装箱成一个Integer),可以使用boxed方法。
final IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
final Stream<Integer> stream = intStream.boxed();
5.7.2 数值范围
假设你想要生成1和100之间所有数字,java8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range 和rangeClosed。这两个方法都是第一个参数接受起始值,第二个参数接受结束值,但range是不包含结束值的。
第6章 用流收集数据
本章内容
- 用Collectors类创建和使用收集器
- 将数据流归约为一个值
- 汇总:归约的特殊情况
- 数据分组和分区

最直接和最常用的收集器是toList静态方法。它会把流中所有的元素收集到一个List中。
6.1.2 预定义收集器
Collectors类提供的工厂方法创建的收集器。它们主要提供三大功能:
- 将流元素归约和汇总为一个值
- 元素分组
- 元素分区
6.2 归约和汇总
final Long howManyDishes = menu.stream().collect(Collectors.counting());
final long count = menu.stream().count();
6.2.1 查找流中的最大值和最小值
final Comparator<Dish> dishComparator = Comparator.comparingInt(Dish::getCalories);
final Optional<Dish> collect = menu.stream().collect(Collectors.maxBy(dishComparator));
第三部分 使用流和lambda进行高效编程
第8章 Collection API的增强功能
8.1 集合工厂
final List<String> friends = Arrays.asList("Raphael", "Olivia");
friends.set(0, "Richard");
friends.add("Thibaut");//UnsupportedOperationException
8.1.1 List工厂
final List<String> friends = List.of("Raphael", "Olivia", "Thibaut");
System.out.println(friends);
friends.add("Chin-Chun");//UnsupportedOperationException
8.2 使用List和Set
Java8中在List和Set接口中引入以下方法
- removeIf 移除集合中匹配指定谓词。实现了List 和Set 的所有类都提供了该方法,
- replaceAll用于List接口中,它使用一个函数(UnaryOperator)替换元素。
- sort也用于List接口中,对列表自身的元素进行排序。
以上所有方法都作用于调用对象本身。换而言之,它们改变的是集合自身。
8.2.1 removeIf
第四部分 无处不在的Java
第13章 默认方法
Java8允许接口内声明静态方法,Java8还引入了一个新功能,叫做默认方法,通过默认方法你可以指定接口方法的默认实现。换句话说,接口能提供方法的具体实现。
第14章 Java模块系统
第五 部分 提升Java的并发性
第15章 CompletableFuture以及反应式编程背后的概念
15.1 为支持并发而不断演进的Java
15.1.1 线程以及更高层的抽象
并行流的迭代是比现实使用线程更高级的概念,换而言之,使用流是对一种线程模式的抽象。这种抽象引入流就像使用一种设计模式,带来的好处是程序员不再需要写枯燥的代码模板,库中的实现隐藏了代码大部分的复杂性。
15.1.2 执行器和线程池
1 线程的问题
Java线程直接访问操作系统的线程。这里主要的问题在于创建和删除操作系统的代价很大,并且一个系统中能创建的线程数目是有限的。如果创建的线程数超过操作系统的限制,很可能导致Java应用莫名其妙地奔溃,因此 不要在线程运行时持续不断地创建新线程。
2 线程池的优势
Java的ExecutorService提供了一个接口,用户可以提交任务并获取它们的执行结果。
第16章 ComplettableFuture 组合式异步编程
第17章 反应式编程
反应式编程让能以异步的方式处理、整合来自不同系统和源头的数据流。以这种方式实现的应用可以在处理数据的同时进行反馈,让数据对用户的响应更及时。
17.1 反应式
- 响应式:反应式系统的响应时间很快,更重要的是它的响应时间应该是稳定且可预测的。
- 韧性——系统在出现失败时仍然能继续响应服务。
- 弹性
- 消息驱动

17.1.2 反应式系统
消息驱动与时间驱动的另一个重要区别是,消息往往是直接发送给一个目标的,而事件会被所有注册了该事件的组件接受。此外,还有一点非常重要,值得特别提一下,反应式系统中的消息是以异的方式发送和接受的。这种方式有效地解耦了发送方和接收方。组件间完全的解耦合既是实现有效隔离的必要前提,也是保证系统在遭遇失效和超大负荷时仍能保持响应的基础。
17.2.1 Flow类
Java9为了支持反应式编程新增了一个类java.util.concurrent.Flow。这个类只包含一个静态组件,无法实例化。Flow类包含了四个嵌套的接口来体现反应式项目定义的标准发布-订阅模型。分别是
- 发布者(Publisher)
- 订阅者(Subscriber)
- 订阅(Subscription)
- 处理者(Processor)
凭借Flow类,相互关联的接口或者静态方法可以构造流(flow-controlled)组件。Publisher生产的元素可以被一个或多个Subscriber消费。发布者和生产者之间关系通过订阅者管理,发布者是顺序事件的提供者,并且这些事件的数量可能没有上线,不过它也背压机制的制约,按照Subscriber的反馈进行元素的生产。
