/

java8 Lambda表达式

Tips

本篇文章主要是RxJava官方文档的辅料,里面太多的Lambda表达式,没有一些了解的确难以看懂,因为是主要的重心还是落在RxJava上,所以本文概述一下如何在Android Studio中使用以及各个表达式的意思,还有与Rxjava及其相似的stream。官方文档如何描述Lambda表达式:

Lambda expressions are a new and important feature included in Java SE 8. They provide a clear and concise way to represent one method interface using an expression. Lambda expressions also improve the Collection libraries making it easier to iterate through, filter, and extract data from a Collection. In addition, new concurrency features improve performance in multicore environments.

这段话指出了在java8中lambda表达式三个主要的作用:简洁的方式呈现接口;提高集合迭代,过滤和扩展的性能;提高多核环境下并发的性能。具体的翻译还是大家自行去理解,中英转换肯定会出现一定的偏差。

软硬件环境

  • JDK 8
  • NetBeans 7.4 (这个只是IDE 在Android Studio中同样是可以使用的)

Lambda应用场景

Anonymous Inner Class

匿名内部类,用过java的人这个应该都知道就不再赘述

1
2
3
4
5
6
7
8
9
10
11
// 匿名内部类
JButton jb1 = new JButton();
jb1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("xxx");
}
});
// lambda表达式的方式
JButton jb = new JButton();
jb.addActionListener((e) -> System.out.println());

Functional Interface

在java中,一个interface里面只有一个需要实现的方法,这种形式的接口就被称作为Functional Interface,也被叫做SAM.(Single Abstract Method)

1
2
3
4
5
6
7
8
public interface ActionListener extends EventListener {

/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e);

}

Lambda Expression Syntax

λ表达式的语法,通常λ表达式可以将5行代码,转换成一行代码来表示,上面的匿名内部类便是例子,λ表达式的语法由如下三个部分组成:

参数列表 箭头 方法体
(int x,int y) -> x + y

lambda表达式实例

Listener Lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void listenerLambda() {
// Listener Lambda
JButton jb1 = new JButton();
jb1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("xxx");
}
});

// lambda表达式的方式
JButton jb = new JButton();
jb.addActionListener((e) -> System.out.println());
}

Comparator Lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static void compatorLambda() {
// sort with inner class
List<Person> personList = new ArrayList<>();
personList.add(new Person("zk"));
personList.add(new Person("ak"));


System.out.println("sort with inner class");
Collections.sort(personList, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
// 按照字典序排列 person
return o1.getSurName().compareTo(o2.getSurName());
}
});

for (Person p:
personList) {
System.out.println(p.getSurName());
}


System.out.println("sort with lambda");
// sort with lambda
Collections.sort(personList, (p1,p2)->p1.getSurName().compareTo(p2.getSurName()));
for (Person p:
personList) {
System.out.println(p.getSurName());
}
}

Runnable Lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static void runnableLambda() {
// 传统模式
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Tran A");
System.out.println("Tran B");
}
};

// lambda
Runnable r2 = () -> System.out.print("Lambda A");
System.out.print("Lambda B");

r1.run();
r2.run();
}

Improving Code with Lambda Expression

使用Lambda优化代码。代码优化这件事可轻可重,像我写代码的主要目的就是奔着功能去,一往无前,有bug回头改,往往都是一去不返。代码优化有很多方面,像6大设计原则,23种设计模式等等,今天得例子要验证得Principle叫做 — ‘Don’t Repeat Yourself ‘ 简称 (DRY),不得不说所有得原则全称都不如简称有格调。

那下面我们就来验证一下lambda相对于原有得做法到底 DRY 在哪里:

1.场景

有一群人来面试,只招聘司机、应征入伍、飞行员,面试得条件如下:

司机 : 年纪超过16
入伍 : 男性年龄在18-25之间
飞行员:年龄在23 到 65之间

Person 字段

1
2
3
4
5
6
7
8
9
public class Person {
private String givenName;
private String surName;
private int age;
private Gender gender;
private String eMail;
private String phone;
private String address;
}

2.创建person List

怎么创建比较随意,我这里就按照官网得方式来创建(在上一篇RxJava中有关于设计模式得附录)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static List<Person> createShortList() {
List<Person> people = new ArrayList<>();
people.add(
new Person.Builder().
givenName("Bob")
.surName("Baker")
.age(21)
.gender(Gender.MALE)
.email("bob.baker@example.com")
.phoneNumber("201-121-4678")
.address("44 4th St, Smallville, KS 12333")
.build()
);
people.add(
new Person.Builder()
.givenName("Jane")
.surName("Doe")
.age(25)
.gender(Gender.FEMALE)
.email("jane.doe@example.com")
.phoneNumber("202-123-4678")
.address("33 3rd St, Smallville, KS 12333")
.build()
);
people.add(
new Person.Builder()
.givenName("John")
.surName("Doe")
.age(25)
.gender(Gender.MALE)
.email("john.doe@example.com")
.phoneNumber("202-123-4678")
.address("33 3rd St, Smallville, KS 12333")
.build()
);

return people;
}
  1. 选出满足条件的人
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class RoboContactMethods {

public void callDrivers(List<Person> pl) {
for (Person p : pl) {
if (p.getAge() >= 16) {
roboCall(p);
}
}
}

public void emailDraftees(List<Person> pl) {
for (Person p : pl) {
if (p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE) {
roboEmail(p);
}
}
}

public void mailPilots(List<Person> pl) {
for (Person p : pl) {
if (p.getAge() >= 23 && p.getAge() <= 65) {
roboMail(p);
}
}
}

public void roboCall(Person p) {
System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
}

public void roboEmail(Person p) {
System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.geteMail());
}

public void roboMail(Person p) {
System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
}
}

我看这段代码是没问题的,for循环迭代选出满足条件的人,完全没毛病;那我们来看看官方的犊子是怎么演的。

  • 没有遵循DRY原则
    • 每个方法的选择条件必须重写
    • 每个方法都重复循环
  • 需要大量方法来实现每个用例
  • 上述代码不灵活,如果招聘条件发生变化,那么将引起大量的更改,可维护性不强

4.第一次重构

这次我们将上述的几个问题列入考虑,其实,我感觉我的翻译貌似出了点小差错,或者是我没明白他是什么意思,除了最后一项可以理解之外,其他的情况没弄懂是什么意思,再或者是本身语言的限制只能优化最后一项,那么下面看看,该如何重构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class RoboContactMethods2 {

public void callDrivers(List<Person> pl) {
for (Person p : pl) {
if (isDriver(p)) {
roboCall(p);
}
}
}

public void emailDraftees(List<Person> pl) {
for (Person p : pl) {
if (isDraftee(p)) {
roboEmail(p);
}
}
}

public void mailPilots(List<Person> pl) {
for (Person p : pl) {
if (isPilot(p)) {
roboMail(p);
}
}
}

public boolean isDriver(Person p) {
return p.getAge() >= 16;
}

public boolean isDraftee(Person p) {
return p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
}

public boolean isPilot(Person p) {
return p.getAge() >= 23 && p.getAge() <= 65;
}

public void roboCall(Person p) {
System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
}

public void roboEmail(Person p) {
System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.geteMail());
}

public void roboMail(Person p) {
System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
}

}

搜索条件被封装进方法中,较之前的代码而言确实有一定的提升,条件可以被反复利用并且可以更方便的更改。但是,还是有很多重复的地方,是不是没有更好的办法呢?(······肯定是有的,要不我说个球)

在使用lambda之前先看看还有什么能改进的或者是为lambda做一些铺垫。上述的代码,其实并不符合开闭原则(OCP – 原则上对修改关闭,对扩展开放),这段程序可能发生改变是招聘条件,那么我们让调用程序自己去维护,就更进一步的优化了。

将条件抽象成接口,让调用者自己去维护,接口如下:

1
2
3
public interface MyTest<T> {
public boolean test(T t);
}

RoboContactsAnon.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.example.lambda;
import java.util.List;
import java.util.function.Predicate;

/**
* @author MikeW
*/
public class RoboContactsAnon {
public void phoneContacts(List<Person> pl, MyTest<Person> pred) {
for (Person p : pl) {
if (pred.test(p)) {
roboCall(p);
}
}
}

public void emailContacts(List<Person> pl, MyTest<Person> pred) {
for (Person p : pl) {
if (pred.test(p)) {
roboEmail(p);
}
}
}

public void mailContacts(List<Person> pl, MyTest<Person> pred) {
for (Person p : pl) {
if (pred.test(p)) {
roboMail(p);
}
}
}

public void roboCall(Person p) {
System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
}

public void roboEmail(Person p) {
System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
}

public void roboMail(Person p) {
System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
}
}

RoboCallTest03.java 调用程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class RoboCallTest03 {

public static void main(String[] args) {

List<Person> pl = Person.createShortList();
RoboContactsAnon robo = new RoboContactsAnon();

System.out.println("\n==== Test 03 ====");
System.out.println("\n=== Calling all Drivers ===");
robo.phoneContacts(pl,
new MyTest<Person>(){
@Override
public boolean test(Person p){
return p.getAge() >=16;
}
}
);

System.out.println("\n=== Emailing all Draftees ===");
robo.emailContacts(pl,
new MyTest<Person>(){
@Override
public boolean test(Person p){
return p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
}
}
);


System.out.println("\n=== Mail all Pilots ===");
robo.mailContacts(pl,
new MyTest<Person>(){
@Override
public boolean test(Person p){
return p.getAge() >= 23 && p.getAge() <= 65;
}
}
);
}
}

仅通过java8之前得方式,程序看起来已经符合OCP的原则;在前面提到过SAM这种形式是可以用,lambda来表示的。那么看看如何去用lambda,来进一步的优化上面的程序

lambda 优化

在java.util.function包中提供了一种形式类似的MyTest的接口,Predicate

1
2
3
public interface Predicate<T> {
public boolean test(T t);
}

RoboContactsLambda.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.example.lambda;
import java.util.List;
import java.util.function.Predicate;

/**
* @authorzhoukan
*/
public class RoboContactLambda {
public void phoneContacts(List<Person> pl, Predicate<Person> pred) {
for (Person p : pl) {
if (pred.test(p)) {
roboCall(p);
}
}
}

public void emailContacts(List<Person> pl, Predicate<Person> pred) {
for (Person p : pl) {
if (pred.test(p)) {
roboEmail(p);
}
}
}

public void mailContacts(List<Person> pl, Predicate<Person> pred) {
for (Person p : pl) {
if (pred.test(p)) {
roboMail(p);
}
}
}

public void roboCall(Person p) {
System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
}

public void roboEmail(Person p) {
System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
}

public void roboMail(Person p) {
System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
}
}

Mytest接口被更换成了Predicate,调用程序的代码片段如下

1
2
3
4
5
6
7
8
public static void main(String[] args){ 

List<Person> pl = Person.createShortList();
RoboContactLambda robo = new RoboContactLambda();

Predicate<Person> allDrivers = p -> p.getAge() >= 16;
robo.phoneContacts(pl, allDrivers);
}

当然即使不适用util.function提供的接口,也是可以实现了,只要满足lambda表达式的场景就好:

1
2
3
4
5
6
7
8
9
robo.mailContacts(pl,
/*new MyTest<Person>(){
@Override
public boolean test(Person p){
return p.getAge() >= 23 && p.getAge() <= 65;
}
}*/
(p) -> p.getAge() > 23 && p.getAge() <= 65
);

The java.util.function 包

function包提供了几种方便去使用lambda的interface,当然不仅仅是给lambda表达式使用,所有诸如上述的优化都是可以考虑用这些方法的,确实我看到这里就有点抑制不住内心的小狂喜,lambda的这些function不就是Rxjava中常用的操作符嘛,这么麻烦的工作算是没白做,就下面的需求,我们来体验一下她的魅力。

  • Predicate: A property of the object passed as argument
  • Consumer: An action to be performed with the object passed as argument
  • Function: Transform a T to a U
  • Supplier: Provide an instance of a T (such as a factory)
  • UnaryOperator: A unary operator from T -> T
  • BinaryOperator: A binary operator from (T, T) -> T

输出东方人和西方人的信息

上面的personList(求职者),要将他们的信息分别发给,老板和老板娘,他们一个是中国人,一个是美国人;由于习惯不同,西方习惯姓在后名在前(分别对应Given name 和 surname),中国的习惯我就不提了。

传统的方式

person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void printWesternName() {

System.out.println("\nName: " + this.getGivenName() + " " + this.getSurName() + "\n" +
"Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" +
"EMail: " + this.geteMail() + "\n" +
"Phone: " + this.getPhone() + "\n" +
"Address: " + this.getAddress());
}

public void printEasternName() {

System.out.println("\nName: " + this.getSurName() + " " + this.getGivenName() + "\n" +
"Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" +
"EMail: " + this.geteMail() + "\n" +
"Phone: " + this.getPhone() + "\n" +
"Address: " + this.getAddress());
}

使用Function Interface的方式

1
2
3
public interface Function<R,T>{
public R apply(T t);
}

指定两个泛型以T作为输入,R作为输出,那么对于上述场景而言 -> Function<string,person>,用function来更灵活的实现打印方法:

Person.java

1
2
3
public String printCustom(Function <Person, String> f){
return f.apply(this);
}

调用程序 NameTestNew.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class NameTestNew {
public static void main(String[] args) {

System.out.println("\n==== NameTestNew02 ===");

List<Person> list1 = Person.createShortList();

// Print Custom First Name and e-mail
System.out.println("===Custom List===");
for (Person person : list1) {
System.out.println(
person.printCustom(p -> "Name: " + p.getGivenName() + " EMail: " + p.getEmail())
);
}


// Define Western and Eastern Lambdas

Function<Person, String> westernStyle = p -> {
return "\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
"Age: " + p.getAge() + " " + "Gender: " + p.getGender() + "\n" +
"EMail: " + p.getEmail() + "\n" +
"Phone: " + p.getPhone() + "\n" +
"Address: " + p.getAddress();
};

Function<Person, String> easternStyle = p -> "\nName: " + p.getSurName() + " "
+ p.getGivenName() + "\n" + "Age: " + p.getAge() + " " +
"Gender: " + p.getGender() + "\n" +
"EMail: " + p.getEmail() + "\n" +
"Phone: " + p.getPhone() + "\n" +
"Address: " + p.getAddress();

// Print Western List
System.out.println("\n===Western List===");
for (Person person : list1) {
System.out.println(
person.printCustom(westernStyle)
);
}

// Print Eastern List
System.out.println("\n===Eastern List===");
for (Person person : list1) {
System.out.println(
person.printCustom(easternStyle)
);
}


}
}

第一次循环只输出了名和email,所有有效的表达式都能作为printCustom的参数;第二次,首先将东方人和西方人的输出方式定义为Fucntion,并当作参数传入printCustom中。lambda表达式利于扩展和反复利用的优势得以体现。

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
==== NameTestNew02 ===
===Custom List===
Name: Bob EMail: bob.baker@example.com
Name: Jane EMail: jane.doe@example.com
Name: John EMail: john.doe@example.com
Name: James EMail: james.johnson@example.com
Name: Joe EMail: joebob.bailey@example.com
Name: Phil EMail: phil.smith@examp;e.com
Name: Betty EMail: betty.jones@example.com

===Western List===

Name: Bob Baker
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333

Name: Jane Doe
Age: 25 Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: John Doe
Age: 25 Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: James Johnson
Age: 45 Gender: MALE
EMail: james.johnson@example.com
Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111

Name: Joe Bailey
Age: 67 Gender: MALE
EMail: joebob.bailey@example.com
Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111

Name: Phil Smith
Age: 55 Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333

Name: Betty Jones
Age: 85 Gender: FEMALE
EMail: betty.jones@example.com
Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333

===Eastern List===

Name: Baker Bob
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333

Name: Doe Jane
Age: 25 Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: Doe John
Age: 25 Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Name: Johnson James
Age: 45 Gender: MALE
EMail: james.johnson@example.com
Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111

Name: Bailey Joe
Age: 67 Gender: MALE
EMail: joebob.bailey@example.com
Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111

Name: Smith Phil
Age: 55 Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333

Name: Jones Betty
Age: 85 Gender: FEMALE
EMail: betty.jones@example.com
Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333

lambda表达式和集合

体验过Function接口的优点还没完,java 8给我们的惊喜还不止这些。(不这些都不够,我必须是你近旁的一只木棉,根紧握地里,也相触云间 … 神特么的乱入),lambda基础部分结束后,来看看在集合方面它又有哪些优势。

先看看有哪些不一样的操作符:

  • forEach
  • stream
  • filter
  • collection
  • map

1.添加新的类,将查询条件全部封装进HashMap中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SearchCriteria {

private final Map<String, MyTest<Person>> searchMap = new HashMap<>();

private SearchCriteria() {
super();
initSearchMap();
}

private void initSearchMap() {
MyTest<Person> allDrivers = p -> p.getAge() >= 16;
MyTest<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
MyTest<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65;

searchMap.put("allDrivers", allDrivers);
searchMap.put("allDraftees", allDraftees);
searchMap.put("allPilots", allPilots);

}

public MyTest<Person> getCriteria(String PredicateName) {
MyTest<Person> target;

target = searchMap.get(PredicateName);

if (target == null) {

System.out.println("Search Criteria not found... ");
System.exit(1);

}

return target;

}

public static SearchCriteria getInstance() {
return new SearchCriteria();
}

}

2.Looping

Test01ForEach.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {

List<Person> pl = Person.createShortList();

System.out.println("\n=== Western Phone List ===");
pl.forEach(p -> p.printWesternName());

System.out.println("\n=== Eastern Phone List ===");
pl.forEach(Person::printEasternName);

System.out.println("\n=== Custom Phone List ===");
pl.forEach(p -> {
System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail()));
});

}

3.链式调用和过滤器

Test02Filter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test02Filter {

public static void main(String[] args) {

List<Person> pl = Person.createShortList();

SearchCriteria search = SearchCriteria.getInstance();

System.out.println("\n=== Western Pilot Phone List ===");

pl.stream().filter(search.getCriteria("allPilots"))
.forEach(Person::printWesternName);


System.out.println("\n=== Eastern Draftee Phone List ===");

pl.stream().filter(search.getCriteria("allDraftees"))
.forEach(Person::printEasternName);

}

}

4.getting Lazy

由于篇幅过长,写的有点疲惫,细枝末节的地方,可能没描述清楚,留言里面见;当然这个getting lazy说的并不是我懒

  • Laziness:跟java-web的懒加载类似的概念,在前面程序中,循环就采取了了‘lazy’的方式,在filter之后再进行输出,这是一种更为高效的方式
  • Eagerness:Code that performs operations on every object in a list is considered “eager”. For example, an enhanced for loop that iterates through an entire list to process two objects, is considered a more “eager” approach.(ps : 直接给原文了)

stream

在上面的代码中,filter之前都会先使用stream,stream方法会将Collection作为输入,返回java.util.stream.Stream接口作为输出。Stream就是集合中元素的序列;默认情况下,一旦元素被调用在stream中将不再有该元素,类似于java中的queuen,因此,每个stream只能被使用一次;另外,stream能变成一个序列在该方法调用之后,后文的例子将印证这一点。

转化和结果

当stream被使用之后被处理,所以在stream中的元素,是无法发生更改;那么如果你还是想在链式操作之后保留stream对应的collection:可以将他们保留在一个新的集合中SHOW CODE :

Test03toList.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test03toList {

public static void main(String[] args) {

List<Person> pl = Person.createShortList();

SearchCriteria search = SearchCriteria.getInstance();

// Make a new list after filtering.
List<Person> pilotList = pl
.stream()
.filter(search.getCriteria("allPilots"))
.collect(Collectors.toList());

System.out.println("\n=== Western Pilot Phone List ===");
pilotList.forEach(Person::printWesternName);

}

}

.collect(Collectors.toList()); 返回一个新的collection

集合中的计算

map()通常和filter成对出现,map会从对象里抽出某个属性,来执行一些操作,下面的例子以age作为主要的操作字段,来呈现集合中计算的用法

Test04Map.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Test04Map {

public static void main(String[] args) {
List<Person> pl = Person.createShortList();

SearchCriteria search = SearchCriteria.getInstance();

// Calc average age of pilots old style
System.out.println("== Calc Old Style ==");
int sum = 0;
int count = 0;

for (Person p:pl){
if (p.getAge() >= 23 && p.getAge() <= 65 ){
sum = sum + p.getAge();
count++;
}
}

long average = sum / count;
System.out.println("Total Ages: " + sum);
System.out.println("Average Age: " + average);


// Get sum of ages
System.out.println("\n== Calc New Style ==");
long totalAge = pl
.stream()
.filter(search.getCriteria("allPilots"))
.mapToInt(p -> p.getAge())
.sum();

// Get average of ages
OptionalDouble averageAge = pl
.parallelStream()
.filter(search.getCriteria("allPilots"))
.mapToDouble(p -> p.getAge())
.average();

System.out.println("Total Ages: " + totalAge);
System.out.println("Average Age: " + averageAge.getAsDouble());

}

}

结语

总算是写完了,博客后面写得不是很走心,但是还是比较开心,又一次通过官方文档,完成了一个知识点:另外由于篇幅问题,Android Studio中如何引入java 8将在另外一篇博客中说明,lambda就到这里了,欢迎勘误。

参考文档

oracle 官方教程