JDK1.8之Lambda表达式

一、Lambda 表达式

1.1 基本格式

1
(参数列表)->{代码}

1.2 匿名内部类方式

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 LambdaDemo01 {
public static void main(String[] args) {
// 传统写法需要传参
RunnableRealization runnable = new RunnableRealization();
Thread thread01 = new Thread(runnable);
/*
稍微优化的【匿名内部类】写法,进一步优化就是lambda写法了
匿名内部类的作用:
1.避免匿名内部类定义过多;
2.使代码看起来简洁
3.简化代码,只留下核心逻辑
*/
Thread thread02 = new Thread(new Runnable() {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@Override
public void run() {
System.out.println("祖安狂人蒙多");
}
});
}
}

/**
* 一次性的类,用在new Thread中充当Runnable对的实现类
*/
class RunnableRealization implements Runnable {
@Override
public void run() {
System.out.println("祖安狂人蒙多");
}
}

示例一

在创建线程并启动时可以使用匿名内部类的写法:

1
2
3
4
5
6
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
}).start();

可以使用 Lambda 的格式对其进行修改。修改后如下:

1
2
3
4
5
6
new Thread(() -> {
System.out.println("hello world");
}).start();

// 最简写法
new Thread(() -> System.out.println("hello world")).start();

示例二

现有方法定义如下,其中 IntBinaryOperator 是一个接口。先使用匿名内部类的写法调用该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
calculateNum(new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
});
}

public static int calculateNum(IntBinaryOperator operator) {
int a = 10;
int b = 20;
return operator.applyAsInt(a, b);
}

Lambda 写法:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
calculateNum((left, right) -> {
return left + right;
});
}

// 最简写法
public static void main(String[] args) {
calculateNum((left, right) -> left + right);
}

示例三

现有方法定义如下,其中 IntPredicate 是一个接口。先使用匿名内部类的写法调用该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
printNum(new IntPredicate() {
@Override
public boolean test(int value) {
return value%2 == 0;
}
});
}

public static void printNum(IntPredicate predicate) {
int[] arr = {1,2,3,4,5,6,7,8,9,10};
for (int i : arr) {
if (predicate.test(i)) {
System.out.println(i);
}
}
}

Lambda 写法:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
printNum((value) -> {
return value%2 == 0;
});
}

// 最简写法
public static void main(String[] args) {
printNum(value -> value%2 == 0);
}

示例四

现有方法定义如下,其中 Function 是一个接口。先使用匿名内部类的写法调用该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
typeConver(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
}

public static <R> R typeConver(Function<String, R> function) {
String str = "123456";
R result = function.apply(str);
return result;
}

Lambda 写法:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
typeConver((s) -> {
return Integer.valueOf(s);
});
}

// 最简写法
public static void main(String[] args) {
typeConver(Integer::valueOf);
}

示例五

现有方法定义如下,其中 Int 是一个接口。先使用匿名内部类的写法调用该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
foreachArr(new IntConsumer() {
@Override
public void accept(int value) {
System.out.println(value);
}
});
}

public static void foreachArr(IntConsumer consumer) {
int[] arr = {1,2,3,4,5,6,7,8,9,10};
for (int i : arr) {
consumer.accept(i);
}
}

Lambda 写法:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
foreachArr((value) -> {
System.out.println(value);
});
}

// 最简写法
public static void main(String[] args) {
foreachArr(System.out::println);
}

二、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
public static void streamDemo() {
Stream stream = Stream.of("a", "b", "c");
String[] strArray = new String[]{"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
List<String> list = Arrays.asList(strArray);
stream = list.stream();
// 一个Stream流只可以使用一次,这段代码为了简洁而重复使用了数次,因此会抛出 stream has already been operated upon or closed 异常
try {
Stream<String> stream2 = Stream.of("a", "b", "c");
// 转换成 Array
String[] strArray1 = stream2.toArray(String[]::new);

// 转换成 Collection
List<String> list1 = stream2.collect(Collectors.toList());
List<String> list2 = stream2.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream2.collect(Collectors.toSet());
Stack stack1 = stream2.collect(Collectors.toCollection(Stack::new));

// 转换成 String
String str = stream.collect(Collectors.joining()).toString();
} catch (Exception e) {
e.printStackTrace();
}
}

2.1 Stream 中间操作符

流方法 含义
filter 用于通过设置的条件过滤出元素
distinct 返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流
limit 会返回一个不超过给定长度的流
skip 返回一个扔掉了前n个元素的流
map 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”)
flatMap 使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流
sorted 返回排序后的流

stream().filter()

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
public static void filterListDemo() {
List<String> accountList = new ArrayList<>();
accountList.add("tom");
accountList.add("jerry");
accountList.add("beita");
accountList.add("shuke");
accountList.add("damu");

// 1.1 业务要求:长度大于等于5的有效账号
for (String account : accountList) {
if (account.length() >= 5) {
System.out.println("有效账号:" + account);
}
}

// 1.2 迭代方式进行操作
Iterator<String> it = accountList.iterator();
while (it.hasNext()) {
String account = it.next();
if (account.length() >= 5) {
System.out.println("it有效账号:" + account);
}
}

// 1.3 Stream结合lambda表达式,完成业务处理
List<String> validAccounts = accountList.stream().filter(s -> s.length() >= 5).collect(Collectors.toList());
System.out.println(validAccounts);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

public static void filterArrayDemo() {
// arrays -> stream
Integer[] nums = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println(Arrays.asList(nums));

// filter(Predicate(T t)->Boolean) 接受一个参数,验证参数是否符合设置的条件
// toArray() 从Stream类型抽取数据转换成数组
Integer[] nums2 = Stream.of(nums).filter(x -> x % 2 == 0).toArray(Integer[]::new);
System.out.println(Arrays.asList(nums2));

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().distinct()

stream().min():取最小值

stream().max():取最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void reduceDemo() {
List<Integer> numList = new ArrayList<>();
numList.add(1);
numList.add(3);
numList.add(2);
numList.add(5);
numList.add(4);
numList.add(6);
numList.add(6);

// min/max/distinct
Integer minNum1 = numList.stream().min((o1, o2) -> {
return o1 - o2;
}).get();
System.out.println(minNum1);
Integer minNum2 = numList.stream().min(Comparator.comparingInt(o -> o)).get();
System.out.println(minNum2);
Integer maxNum = numList.stream().max((o1, o2) -> o1 - o2).get();
System.out.println(maxNum);
numList.stream().distinct().forEach(System.out::println);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().limit()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void reduceDemo() {
List<Integer> numList = new ArrayList<>();
numList.add(1);
numList.add(3);
numList.add(2);
numList.add(5);
numList.add(4);
numList.add(6);
numList.add(6);

// limit
List<Integer> limitNum = numList.stream().limit(2).collect(Collectors.toList());
System.out.println(limitNum);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().skip()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void reduceDemo() {
List<Integer> numList = new ArrayList<>();
numList.add(1);
numList.add(3);
numList.add(2);
numList.add(5);
numList.add(4);
numList.add(6);
numList.add(6);

// skip
List<Integer> limitNum2 = numList.stream().skip(2).collect(Collectors.toList());
System.out.println(limitNum2);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().map()

接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素。

(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”)

map 是对流中的每一个元素进行处理。

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
public static void mapDemo() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
// list -> stream
System.out.println(list);

// map(Function(T, R)-> R) 接受一个参数,通过运算得到转换后的数据
// collect()
List<Double> list2 = list.stream().map(x -> Math.pow(x, 2)).collect(Collectors.toList());
System.out.println(list2);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");

List<String> nameList = Arrays.asList("zs", "ls", "ww", "zl");
// 字母转大写
nameList.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");

// 转换数据类型
list.stream().map(String::valueOf).collect(Collectors.toList()).forEach(System.out::println);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");

// 获取平方
list.stream().map(n -> n*n).collect(Collectors.toList()).forEach(System.out::println);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");

List<String> strList = Arrays.asList("abc", "abc", "bc", "efg", "abcd", "jkl", "jkl");
List<String> resultList = strList.stream().map(str -> str + "-itcast").collect(Collectors.toList());
System.out.println(resultList);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().flatMap()

使用 flatMap 方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用 map(Arrays::stream) 时生成的单个流都被合并起来,即扁平化为一个流。

flatMap 流扁平化,就是把流中的每一个元素都转化成另一个流,然后把所有流汇聚起来成一个流。

1
2
3
4
5
6
7
8
public static void flatMapDemo() {
List<String> strList = Arrays.asList("a bc", "ab c", "b c", "efg", "abcd", "jkl", "jkl");
// List<String> resultList = strList.stream().flatMap(x -> Arrays.asList(x.split(" ")).stream()).collect(Collectors.toList());
List<String> resultList = strList.stream().flatMap(x -> Arrays.stream(x.split(" "))).collect(Collectors.toList());
System.out.println(resultList);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().sorted()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void reduceDemo() {
List<Integer> numList = new ArrayList<>();
numList.add(1);
numList.add(3);
numList.add(2);
numList.add(5);
numList.add(4);
numList.add(6);
numList.add(6);

// sorted().一般在skip/limit或者filter之后进行
List<Integer> sortedNum = numList.stream().skip(2).limit(5).sorted().collect(Collectors.toList());
System.out.println(sortedNum);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

2.2 Stream 终止操作符

流方法 含义
anyMatch 检查是否至少匹配一个元素,返回boolean
allMatch 检查是否匹配所有元素,返回boolean
noneMatch 检查是否没有匹配所有元素,返回boolean
findAny 将返回当前流中的任意元素
findFirst 返回第一个元素
forEach 遍历流
collect 收集器,将流转换为其他形式
reduce 可以将流中元素反复结合起来,得到一个值
count 返回流中元素总数

stream().anyMatch()

stream().allMatch()

stream().noneMatch()

stream().findAny()

stream().findFirst()

stream().forEach()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void forEachDemo() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
// forEach: 接受一个lambda表达式,在Stream每个元素上执行指定的操作
list.stream().filter(n -> n % 2 == 0).forEach(System.out::println);

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");

Map<String, String> map = new HashMap<>();
map.put("a", "a");
map.put("b", "b");
map.put("c", "c");
map.put("d", "d");
map.forEach((k, v) -> System.out.println("k=" + k + ",v=" + v));

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().collect()

stream().reduce()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void reduceDemo() {
List<Integer> numList = new ArrayList<>();
numList.add(1);
numList.add(3);
numList.add(2);
numList.add(5);
numList.add(4);
numList.add(6);
numList.add(6);

// reduce
Optional<Integer> sum1 = numList.stream().reduce((x, y) -> x + y);
System.out.println(sum1.get());
Optional<Integer> sum2 = numList.stream().reduce(Integer::sum);
System.out.println(sum2.get());

System.out.println("~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");
}

stream().count()

三、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
public class LambdaDemo02 {
public static void main(String[] args) {
MobilePhone phone;
// 所有的Lambda的类型都是一个接口,而Lambda表达式本身,就是这个接口的实现
phone = () -> {
System.out.println("开机!");
};
phone.powerOn();
}
}

interface MobilePhone {
/**
* 开机
*/
void powerOn();
}

class OppoPhone implements MobilePhone {
/**
* 开机
*/
@Override
public void powerOn() {
System.out.println("Oppo手机开机!!");
}
}

如果定义成实现类,就会报错

四、Lambda:方法与构造函数引用

4.1 实例对象::实例方法(类名::方法名)

如果两个方法,除了方法名之外,方法参数和返回类型都一致,就称为:方法签名一致

如果某个方法和接口里定义的函数恰好一致,就可以直接传入方法引用。

有点接口方法懒得实现了,然后直接借用其他方法的意思

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LambdaDemo03 {
public static void main(String[] args) {
// 原始的lambda写法
parseIntNum parseIntNum1 = (str) -> Integer.parseInt(str);
System.out.println(parseIntNum1.pass("1"));
// 改进型lambda写法
parseIntNum parseIntNum2 = Integer::parseInt;
System.out.println(parseIntNum2.pass("1"));
}
}

/**
* 接口定义
*/
interface parseIntNum {
// 定义一个String转化成Integer的方法
int pass(String s);
}

如果接口中存在有多个抽象方法,那么就不能直接通过接口来匹配到具体的函数,报错:is not a functional interface

4.2 数据类型::new(方法返回值::new)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LambdaDemo04 {
public static void main(String[] args) {
// 创建长度为10的数组
// 原始的lambda写法
IntFunction<int[]> arr1 = new IntFunction<int[]>() {
@Override
public int[] apply(int num) {
return new int[num];
}
};
arr1.apply(10);
// 改进型lambda写法
IntFunction<int[]> arr2 = int[]::new;
arr2.apply(10);
}
}

五、Lambda:作用域

5.1 基础类型变量

传入 lambda 的变量被隐式 final 所修饰,不能被修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LambdaDemo05 {
public static void main(String[] args) {
String temp = "666";
StrToInt05 sti = (str -> Integer.parseInt(str + temp));
System.out.println(sti.change(temp));
// temp = "000";
// System.out.println(sti.change(temp));
}
}

/**
* 接口定义
*/
interface StrToInt05 {
/**
* 定义一个String转化成Integer的方法
*/
int change(String s);
}

5.2 引用类型变量

如果是引用类型的话就不会报错,因为 Lambda 能够感知外部对引用类型变量的改变,不会出现数据不同步的问题(静态变量和实例变量也是不会报错)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LambdaDemo06 {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("111");
StrToInt06 sti = (str -> Integer.parseInt(list.get(0)));
System.out.println(sti.change("000"));
list.set(0, "666");
System.out.println(sti.change("000"));
}
}

/**
* 接口定义
*/
interface StrToInt06 {
/**
* 定义一个String转化成Integer的方法
*/
int change(String s);
}

六、Lambda:访问局部变量

七、Lambda:访问对象字段与静态变量

八、Lambda:访问接口的默认方法





  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 Liangxj
  • 访问人数: | 浏览次数: