1. 基础知识
通过行为参数化传递代码 - 处理频繁变更的需求。类实现接口,不同的类接口方法的实现不同,作为谓词进行传递处理不同的业务。
List自带了一个sort方法(你也可以使用Collections.sort)。sort的参数类型为函数式接口,所以sort的行为可以用java.util.Comparator对象来参数化:
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
inventory.sort(
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
线程执行代码块:
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
GUI事件处理:
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));
Lambda表达式可以在用到函数式接口的地方使用,接口还可以拥有默认方法既在类没有对方法进行实现时,其主体方法提供默认实现的方法,哪怕有很多默认方法,只要接口只定义了一个抽象方法,它仍然是一个函数式接口。Lambda表达式允许使用内联的方式为函数式接口的抽象方法提供实现,使用匿名内部类也可以实现同样的事情。
Predicate 断言:
java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。
Consumer:
java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。
Function:
java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。
使用局部变量:
不是参数,而是外层作用域中定义的变量,lambda可以没有限制的捕获实例变量和静态变量,但是局部变量必须显示声明为final,或事实是final,其实也就是lambda只能捕获指派给他们的局部变量一次。
下边的代码无法编译,因为局部变量被赋值两次。
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;
为什么局部变量会有这些限制呢?因为局部变量保存在栈上,实例变量保存在堆上。如果lambda可以直接访问局部变量,而且lambda是在一个线程中使用,则使用lambda的线程,可能会在分配该变量的线程将这个变量回收之后去访问该变量。因此java在访问***局部变量时,实际上是在访问它的副本,而不是访问原始变量,如果局部变量仅仅赋值一次那就没什么区别了,因此有了这个限制。使用外部变量的命令式编程会阻碍很容易做到的并行处理。lambda是対值封闭,而不是对变量封闭,这种限制存在的原因在于局部变量保存在栈上,并且隐式表示他们仅限于其所在线程,如果允许改变可改变的局部变量,就会引发造成线程不安全的可能性,实例变量可以,因为它们保存在堆中,而堆是在线程之间共享的。
方法引用 -- lambda的快捷写法
重复使用现有方法,并向lambda一样传递它们。根据已有的方法实现来创建lambda表达式。一般用来替换lambda表达式函数体中只有一句函数体的lambda,类型 + 分隔符 + 方法。
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);
lambda和方法引用实战:
法法引用
inventory.sort(comparing(Apple::getWeight));
传递代码
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
lambda表达式
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight())
);
复合lambda表达式
比较器复合
Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
逆序
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
0
比较器链
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
1
谓词复合
谓词接口包括三个方法:negate,and,or
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
2
and 和 or方法是按照在表达式链中的位置,从左向右确定优先级的,因此,a.or(b).and(c)可以看做(a || b ) && c
复合函数
复合函数说的是可以将function接口所代表的lambda表达式复合起来,function接口为此提供了andThen和compose两个默认方法,他们都会返回function的一个实例。
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
3
2. 函数式数据处理
流:
流可以允许你以声明式的方式处理集合(通过查询语句来表达,而不是临时表写一个实现)要处理大量元素,提高性能,需要并行处理,利用多核架构,无需写任何多线程代码。
流处理集合可以避免垃圾变量(作为一次性中间容器),实现细节放在了它本该归属的库里了。代码以声明式的方式编写而不是如何实现一个操作,将几个基础操作链接起来,来表达复杂的数据处理流水线。
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
4
中间操作,终端操作
返回一个非Stream的值为终端,返回一个Stream的值为中间操作。
使用流:
一个数据源(如集合)来执行一个查询,一个中间操作链形成一条流的流水线,一个终端操作执行流水线并生成结果。
筛选
filter方法接收一个谓词作为参数,返回一个包裹所有复合谓词的元素的流。
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
5
筛选各异的元素
distinct方法,他会返回一个元素各异(hashCode,equals方法)的流。
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
6
截短流
limit(n)方法,返回一个不超过给定长度的流。
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
6
跳过元素
skip(n),返回一个扔掉钱n哥元素的流,如果流元素不足n个,则返回一个空流。limit和skip是互补的。
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
8
映射
map方法,接收一个函数作为参数,这个函数会被应用到每个元素上,并将其映射为一个新的元素
// java.lang.Runnablepublic interface Runnable{
public void run();
}
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
9
流的扁平化
单词列表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));0
flatMap的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容,所有使用map(Arrays::stream)时生成的单个流都被合并起来,既扁化为一个流,其实就是对流使用合并流。
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));1
给定列表[1, 2, 3]和列表[3, 4],应该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单起见,你可以用有两个元素的数组来代表数对。
你可以使用两个map来迭代这两个列表,并生成数对。但这样会返回一个Stream<Stream<Integer[]>>。你需要让生成的流扁平化,以得到一个Stream<Integer[]>。这
正是flatMap所做的:
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));2
查找和匹配
检查谓词是否至少匹配一个元素
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));3
检查谓词是否匹配所有元素
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));4
和allMatch相对的是noneMatch。它可以确保流中没有任何元素与给定的谓词匹配。比如:你可以用noneMatch重写前面的例子:
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));5
anyMatch、allMatch和noneMatch这三个操作都用到了我们所谓的短路,这就是大家熟悉的Java中&&和||运算符短路在流中的版本。
查找元素
findAny方法将返回当前流中的任意元素。它可以与其他流操作结合使用。比如,你可能想找到一道素食菜肴。你可以结合使用filter和findAny方法来实现这个查询:
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));6
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));7
查找第一个元素
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));8
归约
将流中所有元素反复结合起来,得到一个值,这样的查询可以被称为归约操作,将流归约成一个值。
元素求和
Button button = new Button("Send");button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));9
reduce还有一个重载的变体,它不接受初始值,但是会返回一个Optional对象:
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;0
最大值和最小值
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;1
map和reduce的链接通常称为map-reduce模式,因google用它进行搜索而出名,同时很容易并行化。map-reduce模式可以用filter对A字段过滤,map对B字段映射,reduce
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;2
找出2011年的所有交易并按交易额排序(从低到高)
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;3
交易员都在哪些不同的城市工作过
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;4
查找所有来自于剑桥的交易员,并按姓名排序
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;5
返回所有交易员的姓名字符串,按字母顺序排序
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;6
有没有交易员是在米兰工作的
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;7
打印生活在剑桥的交易员的所有交易 -- filter-map-forEach
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;8
所有交易中,最高的交易额是多少 -- reduce
int portNumber = 1337;Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;9
数值流
reduce计算流元素的总和。
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);0
原始类型流特化
IntStream,DoubleStream,LongStream 分别将流中元素转换为int,double,long,从而避免了暗含的装箱成本。么个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max。
映射到数值流
将流转换为特化版本的常用方法是mapToInt,mapToDouble,mapToLong,这些方法和前边说的map方法的工作方式一样,只是他们返回的是一个特化流,而不是Stream<T>
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);1
转换为对象流
一旦有了数值流,可能会想将它转换回非特化流,例如,IntStream上的操作只能产生原始整数:IntStream的map操作接受的lambda必须接受int并返回int,但是如果想要生成另一类值,如Dish,因此,你需要访问的Stream接口中定义的那些更广义的操作,要将原始流转换成一般流(每个int都会装箱成一个Integer),可以使用boxed()方法。
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);2
默认值OptionalInt
求和的例子很容易,因为他有一个默认值0,但是,如果计算IntStream中的最大值就得换个法子了,因为0是错误的结果,如何区分没有元素的流和最大值真是0的流呢,前边说的Optional类,是一个可以表示值存在或不存在的容器,Optional可以用Integer,String等参考类型来参数化。对于三种原始流特化,也分别有一个Optional原始类型特化版本:OptionalInt,OptionalDouble和OptionalLong,例如求IntStream中的最大元素,可以调用max方法,它会返回一个OptionalInt
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);3
如果没有最大值的话,就可以显示处理OptionalInt去定义一个默认值了:
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);4
数值范围
java8引入两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rnageClosed,这两个方法都是第一个参数接收起始值,第二个参数接收结束值,但range是不包含结束值得,而rangeClosed则包含结束值
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);5
生成100个Dish对象
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);6
数值流应用:勾股数
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);7
对每个给定的a值创建一个三元数流,要把a的只映射到三元数流的话,就会得到一个由流构成的流,flatmap方法在做映射的同时,还会把所有生成的三元数流扁平化成一个流,既这个流中的数据在下一个流中使用也就是将由流构成的流扁平化。而IntStream中的map方法只能为流中的每一个元素返回一个int,可以使用mapToObj方法返回一个对象值流。
求两次平方根,如果让代码更加紧凑的一种可能方法是,先生成所有的三元数,然后在筛选复合条件的:
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);8
由值创建流
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);9
由数组创建流
inventory.sort(comparing(Apple::getWeight));0
由文件生成流
Files.lines,它会返回一个由指定文件中的各行构成的字符串流。
inventory.sort(comparing(Apple::getWeight));1
由函数生成流:创建无限流
Stream API提供了两个静态方法从函数来生成流:Stream.iterate,Stream.generate这两个操作可以创建所谓的无限流,不像是从固定集合创建的流那样有固定大小的流。这两个函数产生的流会用给定的函数按需创建值,并且可以无穷地计算下去,一般情况下要用limit(n)来对这种流加以限制,来避免打印无穷多个值。
inventory.sort(comparing(Apple::getWeight));2
iterate方法接收一个初始值,和一个依次应用在每个产生的新值上的lambda(unaryOperator<T>类型,一元运算符)。然后调用forEach来消费流。
斐波纳契元组序列::(0, 1),(1, 1), (1, 2), (2, 3), (3, 5), (5, 8), (8, 13), (13, 21) …
inventory.sort(comparing(Apple::getWeight));3
用流收集数据
流可以看做花哨又懒惰的数据集迭代器,支持两种类型的操作:中间操作(filter或map)和终端操作(count,findFirst,forEach,reduce)。
由Transaction构成的List,并且想按照名义货币进行分组。目标map,源集合,遍历集合,map里边拿数据,不能拿到则创建,添加到map,小集合增加数据。
inventory.sort(comparing(Apple::getWeight));4
inventory.sort(comparing(Apple::getWeight));5
这里的groupingBy是Collectior接口的一个实现。
预定义收集器:
预定义收集器也就是那些可以从Collectors类提供的工厂方法创建的收集器主要提供了三大功能:将流元素归约和汇总为一个值,元素分组,元素分区。
归约和汇总:菜单里有多少菜种:
inventory.sort(comparing(Apple::getWeight));6
也可以这样写:
inventory.sort(comparing(Apple::getWeight));6
查询流中的最大值和最小值:Collectors.maxBy,Collectors.minBy这两个收集器接收一个Comparator参数来比较流中的元素。
inventory.sort(comparing(Apple::getWeight));8
汇总:Collectors.summingInt,它可以接收一个把对象映射为求和所需int的函数,并返回一个收集器。
inventory.sort(comparing(Apple::getWeight));9
汇总不仅仅是求和,还有Collectors.averagingInt,averagingLong,averagingDouble等可以计算平均值:
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());0
通过收集器给流中的元素计数,可以找到这些元素值属性的最大值,最小值,总和,平均数。如果想只需一次操作就可以完成上述所有功能,可以使用summarizingInt工厂方法返回的收集器。
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());1
这个收集器会把所有这些信息收集到一个叫作IntSummaryStatistics的类里,它提供了方便的取值(getter)方法来访问结果。打印menuStatisticobject会得到以下输出:
IntSummaryStatistics{count=9, sum=4300, min=120,average=477.777778, max=800}
同样,相应的summarizingLong和summarizingDouble工厂方法也有相关的LongSummaryStatistics和DoubleSummaryStatistics类型。
链接字符串
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());2
广义的归约汇总
事实上,我们已经讨论的所有收集器,都是一个可以用reducing工厂方法定义的归约过程的特少情况而已。Collectors.reducing工厂方法是所有这些特殊情况的一般化。例如用reducing方法创建收集器来计算你菜单的总热量:
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());3
第一个参数时归约操作的起始值,也就是流中没有元素时的返回值,对于数值而言0是一个合适值。
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());4
第二个参数是将菜肴转换成一个表示其所含热量的int。
第三个参数是一个BinaryOperator,将两个项目累积成一个同类型的值,这里就是对两个int求和。
特殊情况,将流中的第一个项目作为起点,返回一个Optional<Dish>对象。
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());5
reduce(Integer::sum)反悔的不是int而是Optional<Integer>,以便在空流的情况下安全地执行归约操作。然后使用Optional对象中的get方法来提取里边的值,如果确认流不为空这种使用get方法是安全的。orElse或orElseGet来解开Optional中包含的值更安全。
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());6
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());7
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());8
第一个无法编译,因为reducing接受的参数是一个BinaryOperator<t>也就是一个BiFunction<T,T,T>,这就意味着他需要的函数必须接受两个参数,然后返回一个相同类型的值,这里的lambda表达式接受的参数是两个菜,返回的确是一个字符串。
分组
Collectors.groupingBy工厂方法返回的收集器可以轻松分组。
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator());9
这里给groupingBy方法传递一个Function(以方法引用的形式)。
分类不一定像方法引用那样可用,因为你想用以分类的条件可能比简单的属性访问器更加复杂,例如:将热量不到400的菜划分为低热量,400-700划分为普通,>700划分为高热量,因为Dish类中没有将这个操作写成一个方法,所以你无法使用方法引用,但是可以将这个逻辑写成lambda表达式:
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});0
多级分组:map嵌套map
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});1
按子组收集数据
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});2
其结果是下面的Map:
{MEAT=3, FISH=2, OTHER=4}
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});3
这个分组的结果显然是一个map,以Dish类型作为键,以包装了该类型中热量最高的Dish的Optional<Dish>作为值:{FISH=Optional[salmon], OTHER=Optional[pizza], MEAT=Optional[pork]}
把收集器的结果转换为另一种类型
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});4
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});5
groupingBy常常联合使用的另一个收集器是mapping方法。这个发方法接收两个参数,一个函数对流中的元素做变化,另一个则将变换的结果对象收集起来
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});6
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});7
分区:
分区是分组的特殊情况,由一个谓词作为分类函数,它称为分区函数,这意味着得到分组map的键类型是Bollean,于是它最多分两组。
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});8
匿名内部类
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});9
{false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]},true={OTHER=[french fries, rice, season fruit, pizza]}}
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight())
);0
{false=pork, true=pizza}
Collectors类的静态工厂方法:toList,toSet,toCollection,counting,sumingInt,averagingInt,sumarizingInt,joining,maxBy,minByh,reducing,collectingAndThen,groupingBy,partioningBy;
七:并行数据处理与性能
并行流:
parallelStream 方法将集合转换为并行流,并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的内容,这样一来,就可以自动把给定操作的工作负荷分配给多核处理器的所有内核。
还没有评论,来说两句吧...