Java8最值得学习的特性就是Lambda表达式和Stream API,如果有python或者javascript的语言基础,对理解Lambda表达式有很大帮助,因为Java正在将自己变的更高(Sha)级(Gua),更人性化。————可以这么说lambda表达式其实就是实现SAM接口的语法糖。

Lambda语法

Lambda的语法极为简单,类似如下结构:
(parameters) -> expression
或者
(parameters) -> { statements; }

Lambda表达式由三部分组成:

  • paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断1。另外当只有一个推断类型时可以省略掉圆括号。
  • ->:可理解为“被用于”的意思
  • 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//示例1:不需要接受参数,直接返回10
()->10
//示例2:接受两个int类型的参数,并返回这两个参数相加的和
(int x,int y)->x+y;
//示例2:接受x,y两个参数,该参数的类型由JVM根据上下文推断出来,并返回两个参数的和
(x,y)->x+y;
//示例3:接受一个字符串,并将该字符串打印到控制到,不反回结果
(String name)->System.out.println(name);
//示例4:接受一个推断类型的参数name,并将该字符串打印到控制台
name->System.out.println(name);
//示例5:接受两个String类型参数,并分别输出,不反回
(String name,String sex)->{System.out.println(name);System.out.println(sex)}
//示例6:接受一个参数x,并返回该该参数的两倍
x->2*x

一.匿名函数

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
package lambdaDemo.com.mission;

/**
* @author mission
* @date 2018/12/3 0003-21:59
*/
public class FunctionDemo {

//方法
public int add(int x,int y){
return x+y;
}


//直接匿名函数
//(int x,int y)->{return x+y;}

//省略方式
//(x,y)->x+y;

//使用例子
//lambda表达式
Runnable run1= ()->{System.out.println("run1---------");};

//普通方法
Runnable run2 =new Runnable() {
@Override
public void run() {
System.out.println("run2---------");
}
};

public static void main(String[] args) {
FunctionDemo demo1 =new FunctionDemo();
demo1.run1.run();
demo1.run2.run();
}
}

结果

1
2
3
4
run1---------
run2---------

Process finished with exit code 0

第一个场景应用

需求:任意2个数的计算

案例:
例一: 方法add(1,2)=1+2=3
例二: 方法mult(1,2)=1*2=2

以前写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package lambdaDemo.com.mission;

/**
* @author mission
* @date 2018/12/4 0004-21:16
*/
public interface Calc {

//+
public int add(int x,int y);

//*
public int mult(int x,int y);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package lambdaDemo.com.mission;

/**
* @author mission
* @date 2018/12/4 0004-21:17
*/
public class Calcimpl implements Calc {
@Override
public int add(int x, int y) {
return x+y;
}

@Override
public int mult(int x, int y) {
return x*y;
}
}

lambda写法

1
2
3
4
5
6
7
8
9
10
11
12
package lambdaDemo.com.mission;

/**
* @author mission
* @date 2018/12/4 0004-21:18
*/
@FunctionalInterface //FunctionalInterface主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
public interface LambaCalc {
//有且仅有一个抽象方法
public int calc(int x,int y);

}

调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package lambdaDemo.com.mission;

/**
* @author mission
* @date 2018/12/4 0004-21:19
*/
public class Main {

public static void main(String[] args) {
//一般以前的使用方式
Calc calc=new Calcimpl();
System.out.println(calc.add(3, 5));
System.out.println("------------------");

//函数式接口
LambaCalc f1=(x, y)->{return x+y;};
LambaCalc f2=(x, y)->x+y;
System.out.println(f1.calc(3, 1));
System.out.println("------------------");
System.out.println(f2.calc(5, 6));

}
}

结果

1
2
3
4
5
6
7
8
------------------
4
------------------
11

Process finished with exit code 0

二.容器遍历方式

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
package lambdaDemo.com.mission;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;

/**
* @author mission
* @date 2018/12/4 0004-21:56
*/
public class FunctionTest {

@Test
public void testList(){
//构造一个容器
String[] args ={"player1","player2","player3","player4","player5"};
//转为List集合
List<String> playerList= Arrays.asList(args);

//以前遍历方式
//外部方式循环,客户代码
for (String player : playerList) {
System.out.println(player);
}

System.out.println("--------------------------");

//lambda方式
//内循环,JDK自己处理
playerList.forEach((player)->{ System.out.println(player); });
System.out.println("--------------------------");
playerList.forEach((player)-> System.out.println(player) );
}
}

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
player1
player2
player3
player4
player5
--------------------------
player1
player2
player3
player4
player5
--------------------------
player1
player2
player3
player4
player5

Process finished with exit code 0

三.容器内容的删除

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
@Test
public void testDeleteList(){
//构造一个容器
String[] args ={"player1","player2","player3","player4","player5"};
//转为List集合...(若用Arrays.asList(args)转换会报错,原因这样list的iterator不支持remove()方法)
List<String> playerList= new ArrayList<>();
for (String player : args) {
playerList.add(player);
}

System.out.println("-------------原来-------------");
playerList.forEach((player)-> System.out.println(player) );


//1.以前删除容器数据方式
Iterator<String> it = playerList.iterator();
while (it.hasNext()){
String player=it.next();
if (player.equals("player2")){
it.remove();
}
}



//2.新方式removeif
playerList.removeIf(new Predicate<String>() {
@Override
public boolean test(String player) {
return player.equals("player3");
}
});



//3.removeif 的lambda表达式
playerList.removeIf((player)->player.equals("player4"));

System.out.println("------------删除后------------");
playerList.forEach((player)-> System.out.println(player) );
}

效果

1
2
3
4
5
6
7
8
9
10
11
-------------原来-------------
player1
player2
player3
player4
player5
------------删除后------------
player1
player5

Process finished with exit code 0

四.集合Stream的操作

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
package lambdaDemo.com.mission;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;

/**
* @author mission
* @date 2018/12/5 0005-16:55
*/
public class CollectionStreamTest {

@Test
public void testStream(){
Integer[] nums={1,2,3,null,4,5,null,6};
List<Integer> list= Arrays.asList(nums);
int sum =0;

//以前累加方式
for (Integer num : list) {
if (num==null) {
continue;
}
sum =sum+num;
}
System.out.println("old sum ->"+sum);

//新方式,流方式,lambda实现
/**
* 链条操作
* 1.list.stream() 创建流
* 2.filter(x->x!=null) 过滤 Stream 中的元素
* 3.reduce((s,x)->s+x) 从一组值中生成一个新的值
*/
sum = list.stream().filter(x->x!=null).reduce((s,x)->s+x).get();
System.out.println("stream sum ->"+sum);


}
}

Stream 中主要包含如下几个方法:

方法名 简介

collect(toList()) 通过 Stream 生成一个列表
map 将流中的一个值转换成一个新的值
filter 过滤 Stream 中的元素
flatMap 将多个 Stream 连接成一个 Stream
max 求最大值
min 求最小值
reduce 从一组值中生成一个新的值

上面就是 Stream 中包含的几个主要方法,下面逐一对其介绍:

collect(toList()) & filter

collect(toList()) 的作用是通过一个 Stream 对象生成 List 对象,案例:

1
2
3
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = list.stream().filter((value) -> value > 2).collect(toList());
result.forEach((value) -> System.out.print(value));

上面的代码先创建了一个 List 对象并初始化,然后筛选出大于 2 的值,输出。
filter 方法的作用是过滤 Stream 中的元素,filter 方法是一个高阶函数,接收一个函数接口作为参数,此高阶函数返回一个 boolean 值,返回 true 的元素会保留下来;
collect(toList()) 方法将 filter 操作返回的 Stream 生成一个 List。

高阶函数:接收或返回一个函数接口的函数称为高阶函数。
函数接口:只包含一个函数的接口成为函数接口。

map

map 函数的作用是将流中的一个值转换成一个新的值,举个例子,我们要将一个 List 转换成 List ,那么就可以使用 map 方法,示例代码:

1
2
3
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<String> result = list.stream().map(value -> String.format("String:%s", value)).collect(toList());
result.forEach(System.out::print);

map 方法将 Integer 元素转换成 String 并使用 collect(toList()) 方法生成一个新的 List。

System.out::print 是 (value) -> System.out.print(value) 的简化版写法。

flatMap

flatMap:将多个 Stream 连接成一个 Stream,这个怎么理解呢,举个栗子:
首先定义一个 List 对象,将这个对象中的每一个 String 都分割成一个字母并生成一个新的 List 对象,代码:

1
2
3
4
5
6
7
8
9
10
List<String> list = Arrays.asList("abc", "def", "ghi");
List<Character> result = list.stream().flatMap(value -> {
char[] chars = value.toCharArray();
Character[] characters = new Character[chars.length];
for(int i = 0; i < characters.length; i++){
characters[i] = chars[i];
}
return Stream.of(characters);
}).collect(toList());
result.forEach(System.out::println);

上面代码先遍历 list ,通过 flatMap 函数将每个 String 元素都生成一个新的 Stream 并将这些 Stream 连接成一个新的 Stream。

max&min

求最大值最小值,这个很好理解了,直接看代码:

1
2
3
4
List<Integer> list = Arrays.asList(0, 1, 2, 3);
Comparator<Integer> comparator = (o1, o2) -> o1.compareTo(o2);
System.out.println(list.stream().min(comparator).get());
System.out.println(list.stream().max(comparator).get());

min 和 max 函数需要一个 Comparator 对象为参数作为比对依据。

reduce

从一组值中生成一个新的值,reduce 函数其实用途非常广泛,作用也比较大,我们举一个累加的例子:

1
2
3
List<Integer> list = Arrays.asList(0, 1, 2, 3);
int count = list.stream().reduce(0, (acc, item) -> acc + item).intValue();
System.out.println(count);

reduce 函数的一个参数为循环的初始值,这里计算累加时初始值为 0,acc 代表已经计算的结果,item 表示循环的每个元素。

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
/**
* 案例一:小写转换为大写
*/
@Test
public void testCase(){
String[] str ={"test","hello","world","java","tom","C","javascript"};

List<String> list=Arrays.asList(str);
list.stream().map(x->x.toUpperCase()).forEach(x-> System.out.println(x));
}

/**
* 案例二:元素扩大两倍
*/
@Test
public void testTwoFold(){
Integer[] data ={1,2,3,4};
List<Integer> list=Arrays.asList(data);
list.stream().map(x->2*x).forEach(x->System.out.println(x));
}

/**
* 案例三:求和
*/
@Test
public void testSum(){
Integer[] data ={1,2,3,4};
List<Integer> list =Arrays.asList(data);
int num =list.stream().reduce((sum,x)->sum+x).get();
System.out.println(num);
}

结果

1
2
3
4
5
6
7
TEST
HELLO
WORLD
JAVA
TOM
C
JAVASCRIPT

1
2
3
4
2
4
6
8
1
10

map和reduce总结

  • map是针对每个数据处理
  • reduce是针对集合最后处理
  • map()进行数据重写组合
  • reduce()是对集合中所有数据变为一个结果

五.Function函数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 输入一个整数类型,返回一个字符串类型
*/
@Test
public void testConvert(){
//以前的方法
Function<Integer,String> fun1 =new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return String.valueOf(integer);
}
};

String s1=fun1.apply(100);
System.out.println(s1);

//lambda使用
Function<Integer,String> fun2=x->String.valueOf(x);
String s2=fun2.apply(2);
System.out.println(s2);

}

结果

1
2
3
4
100
2

Process finished with exit code 0

六.Consumer用法

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
/**
* Consumer<T>
* 定义:
* 代表了接受一个输入参数并且无返回的操作,如果某一类操作不需要返回接口,可以对该类操作抽取逻辑
*
* 方法:
* 无返回函数操作 void accept(T t);
*
* 案例:
* 遍历容器forEach()方法
*
* default void forEach(Consumer<? super T> action) {
* Objects.requireNonNull(action);
* for (T t : this) {
* action.accept(t);
* }
* }
*/
@Test
public void testForEach(){
String[] str={"A1","B2","C3","D4"};
List<String> list= Arrays.asList(str);
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("----------------------------");
list.forEach((x)-> System.out.println(x));
}

效果:

1
2
3
4
5
6
7
8
9
10
11
A1
B2
C3
D4
----------------------------
A1
B2
C3
D4

Process finished with exit code 0

七.Predicte用法

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
/**
*Predicte<T>
* 定义:
* Predicte函数式接口的主要作用是提供一个test方法,接受一个参数返回布尔类型
*
* 方法:
* 函数操作boolean test(T t)
*
* 案例:
* list.stream().filter()方法
*
* @FunctionalInterface
* public interface Predicate<T> {
* boolean test(T t);
* ......
* }
*/
@Test
public void testFitler(){
//容器中数据过滤掉
String[] str={"A1","B2","C3","D4"};
List<String> list= Arrays.asList(str);

list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return !s.equals("C3");
}
}).forEach(t-> System.out.println(t));

System.out.println("----------------------------");
list.stream().filter(x->!x.equals("D4")).forEach(x-> System.out.println(x));
}

效果:

1
2
3
4
5
6
7
8
9
A1
B2
D4
----------------------------
A1
B2
C3

Process finished with exit code 0

本文到此结束