1、lambda表达式入门:Java多核编程



第1章 新生代Java

1、外部迭代 -> 内部迭代

外部迭代示例

1
2
3
for (Point p : pointList) {
p.distance(0,0); // 点p 到 原点(0,0)的距离
}

内部迭代示例

1
pointList.forEach(p -> p.distance(0,0));

1.1、内部迭代

外部迭代 -> 串行、有序

内部迭代 -> 顺序不重要,重要的是行为;

​ -> 延迟加载,乱序执行;

1.2、命令模式

类似与命令模式,将 行为 抽象出来,作为Action;

1
2
3
4
5
6
7
8
9
10
11
12
// 行为 接口
public interface Consumer<T> {
void accept(T t);
}
// 具体定义行为
public Distance implements Consumer<Point> {
public void accept(Point p) {
p.distance(0, 0);
}
}
// 为了调用 p.distance(0, 0); 不断创建对象,而且不够直观
pointList.forEach(new Distance());

为了调用 p.distance(0, 0); 不断创建对象,而且不够直观。

这是因为Java 方法只接受 对象引用 作为参数,因此要引入新语法 lambda 表达式

1.3、lambda 表达式

1
2
3
4
5
pointList.forEach(new Consumer<Point> {
public void accept(Point p) {
p.distance(0, 0);
}
});

Consumer 是一种 ”单方法接口“,有利于 编译器直接 无歧义 的选择正确的方法执行;

引入一个额外的愈发(”->“)之后,将 参数与 表达式 分隔开,得到简单形式的 lambda表达式:

1
pointList.forEach(p -> p.distance(0,0));

2、集合 -> 流

场景:一个Integer集合 —> 转换成一个 Point实例 集合 —> 寻找 距离原点(0, 0) 最远的点到原点的距离;

集合的写法

1
2
3
4
5
6
7
8
9
List<Integer> intList = Arrays.asList(1,2,3,4,5);
List<Point> pointList = new ArrayList<>();
for (Integer i : intList) {
pointList.add(new Point(i%3, i/1));
}
Double maxDistance = Double.MIN_VALUE;
for (Point p : pointList) {
maxDistance = Math.max(maxDistance, p.distance(0, 0));
}

假如把 intList 看作一个无限流:

1
2
3
4
5
intStream = intList.stream();
Stream<Point> pointStream = intStream.map(i -> new Point(i%3, i/1));
DoubleStream distanceStream = pointStream.mapToDouble(p -> p.distance(0, 0));
// 管道最后是终止操作max
OptionalDouble maxDistance = distances.max();

完整的代码变成了如下:

1
2
3
4
5
OptionalDouble maxDistance = intList
.stream()
.map(i -> new Point(i%3, i/1))
.mapToDouble(p -> p.distance(0, 0))
.max();

使用管道的好处是, 创建与管理中间集合所导致的性能消耗也随之消失;


3、串行 -> 并行

Java 8 支持对集合的并行处理,而 lambda表达式 是”提供这种支持“ 必备的一环;

原理:

  1. 将大任务 递归的 切分为 ”足够小“ 的 小任务(能串行执行为止);
  2. 使用 cpu 的多核 分别执行完以后;
  3. 将结果 结合起来 返回;

实现方式:fork/join 框架

代码:

1
2
3
4
5
OptionalDouble maxDistance = intList
.parallelStream()
.map(i -> new Point(i%3, i/1))
.mapToDouble(p -> p.distance(0, 0))
.max();

什么是 “足够小”的任务? 取决于 可用核的数量;


4、组合行为

Java 原版的排序器

1
2
3
4
5
Comparator<Point> byX = new Comparator<Point>() {
public int compare(Point p1, Point p2) {
return Double.compare(p1.getX(), p2.getX());
}
}

创建一个通用的 排序器 Comparator

1
2
3
4
5
public static <T,U extends Comparable<U>> 
Compatable<T> comparing(Function<T, U> keyExtractor) {
return (c1, c2) ->
keyExtractor.apply(c1).compateTo(keyExtractor.apply(c2));
}

那么 1.3 中的问题,根据距离排序 可以实现为

1
Comparator<Point> byDistance = comparing(p -> p.distance(0, 0));

新需求:按所有点到 原点的距离 升序排列,打印点:

1
2
3
4
5
intList
.stream()
.map(i -> new Point(i%3, i/1))
.sorted(comparing(p -> p.distance(0, 0)))
.forEach(p -> System.out.printf("(%f, %f)", p.getX(), p.getY()));