Java8新特性

Lambda表达式

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁,灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

Lambda表达式的格式

1.标准格式:

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

2.格式说明:

​ - 小括内的语法与传统方法参数列表一致,没有参数就留空,有多个参数就用逗号分隔

​ - 【->】 是新引入的语法格式,代表指向动作

​ - 大括号内的语法与传统方法体要求一致

3.案例说明

第一个线程案例

1
2
3
4
5
6
7
8
9
10
11
12
13
Thread thread1 = new Thread(new Runnable() {
@Override
public void run () {
System.out.println("线程需要执行的任务代码1");
}
});
thread1.start();

// Lambda表达式
Thread t2 = new Thread(()->{
System.out.println("线程需要执行的任务代码2");
});
t2.start();

第二个比较器案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Integer> list = new ArrayList<>();
Collections.addAll(list,11,22,33,44,55);
System.out.println("排序之前的集合:" + list);

// 比较器的正常书写格式
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare (Integer o1, Integer o2) {
return o2-o1;
}
});

// Lambda表达式
Collections.sort(list,(Integer o1, Integer o2)->{return o2-o1;});
//或者
list.sort((o1,o2)->(o1-o2));
System.out.println("排序之后的集合:" + list);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class sortTest {
public static void main(String[] args) {
int[][] arr ={{2,6},{1,3},{8,10},{15,18}};
Arrays.sort(arr,(x,y)->(x[0]-y[0]));
for (int[] ints : arr) {
for (int anInt : ints) {
System.out.print(anInt+" ");
}
System.out.println();
}
}
}
输出:
1 3
2 6
8 10
15 18
排序成功!

Lambda表达式的使用条件

  • Lambda表达式不是万能的,他需要函数式接口的支持;
  • 什么是函数式接口:
    函数式接口的定义是: 只包含一个抽象方法的接口,称为函数式接口;
    其实我们的Lambda表达式就是对函数式接口的一种简写方式,所以只有是函数式接口,我们才能用Lambda表达式;再换句话说,Lambda表达式需要函数式接口的支持,那函数式接口我们可以自己定义,当然JDK1.8也给我们提供了一些现成的函数式接口;

首先,都是接口; 其次,接口中有且只有一个接口,才可以使用lambda表达式

​ 1.接口中只有一个抽象方法的接口,叫做函数式接口

​ 2.如果是函数式接口,那么就可以用@FunctionalInterface注解标识

方法引用

先来看一下什么是方法引用:

  • 方法引用其实是Lambda表达式的另一种写法,当要传递给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
import java.util.function.Consumer;

public class MyTest {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("aaaaaaaaaaaaaa");
//aaaaaaaaaaaaaa

//简写1:
Consumer<String> consumer1 = (String s) -> {
System.out.println(s);
};
consumer1.accept("abc");
//abc

//简写2:
Consumer<String> consumer2 = (s) -> System.out.println(s);
consumer2.accept("bcd");
//bcd

//简写3:
Consumer<String> consumer3 = System.out::println;
consumer3.accept("abc");
//abc
}
}

类::静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.function.BinaryOperator;

public class MyTest1 {
public static void main(String[] args) {
BinaryOperator<Double> operator = new BinaryOperator<Double>(){
@Override
public Double apply(Double o, Double o2) {
return Math.max(o,o2);
}
};

System.out.println(operator.apply(2.13, 3.12));//3.12

BinaryOperator<Double> operator2 = (o, o2) -> Math.max(o,o2);
System.out.println(operator2.apply(2.13, 3.12));//3.12

BinaryOperator<Double> operator3 = Math::max;

Double max = operator3.apply(5.0, 20.0);
System.out.println(max);//20.0

}
}

因为Math.max()所需要的参数以及返回值与重写的accpet()一样,因此可以简写为类::静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Comparator;
public class MyTest2 {
public static void main(String[] args) {
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};

System.out.println(comparator.compare(20, 12));//1

Comparator<Integer> comparator1 = Integer::compareTo;
System.out.println(comparator1.compare(20, 12));//1
}
}

类::实例方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Comparator;
public class MyTest2 {
public static void main(String[] args) {
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};

System.out.println(comparator.compare("20", "12"));//1

Comparator<String> comparator1 = String::compareTo;
System.out.println(comparator1.compare("20", "12"));//1
}
}

为什么可以这样写?、
传递过来的两个参数,一个作为调用者,一个作为参数,这时候,使用类::实例方法简写

Stream流

现有一个需求:

将list集合中姓张的元素过滤到一个新的集合中

然后将过滤出来的姓张的元素中,再过滤出来长度为3的元素,存储到一个新的集合中

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
// 已知的知识来解决需求
List<String> list1 = new ArrayList<>();
list1.add("张老三");
list1.add("张小三");
list1.add("李四");
list1.add("赵五");
list1.add("张六");
list1.add("王八");

ArrayList<String> list2 = new ArrayList<>();
// 1.将list集合中姓张的元素过滤到一个新的集合中
for(String name : list1){
if(name.startsWith("张")){
list2.add(name);
}
}
ArrayList list3 = new ArrayList();
for (String name : list2) {
if (name.length() == 3){
list3.add(name);
}
}
System.out.println(list3);

输出结果:
[张老三, 张小三]

2.用Stream流操作集合,获取流,过滤操作,打印输出

1
2
3
list1.stream().filter((String name)->name.startsWith("张")).filter((String name)->name.length()==3).forEach((String name)->{
System.out.println("符合条件的姓名:" + name);
});

( 看不懂没关系,下面会讲到该方法,这里只是用来引入的)

Stream流的格式

1
2
3
4
5
Stream<T> filter(Predicate<? super T> predicate);
-----> 参数:public interface Predicate<T> (函数式接口)
----> 抽象方法:boolean test(T t);
-----> 参数:public interface Consumer<T> (函数式接口)
----> 抽象方法:boolean test(T t);

获取流

Collection接口中有一个stream()方法,可以获取流

1
default Stream<E> stream()

代码演示

1.根据List集合获取流

1
2
3
4
5
6
7
8
9
10

// 创建List集合
List<String> list = new ArrayList<>();
list.add("张老三");
list.add("张小三");
list.add("李四");
list.add("赵五");
list.add("张六");
list.add("王八");
Stream<String> stream1 = list.stream();

2.根据Set集合获取流

1
2
3
4
5
6
7
8
9
// 创建List集合
Set<String> set = new HashSet<>();
list.add("张老三");
list.add("张小三");
list.add("李四");
list.add("赵五");
list.add("张六");
list.add("王八");
Stream<String> stream2 = set.stream();

3.根据Map集合获取流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建Map集合
Map<Integer,String> map = new HashMap<>();
map.put(1,"张老三");
map.put(2,"张小三");
map.put(3,"李四");
map.put(4,"赵五");
map.put(5,"张六");
map.put(6,"王八");

// 3.1根据Map集合的键获取流
Set<Integer> map1 = map.keySet();
Stream<Integer> stream3 = map1.stream();
// 3.2根据Map集合的值获取流
Collection<String> map2 = map.values();
Stream<String> stream4 = map2.stream();
// 3.3根据Map集合的键值对对象获取瑞
Set<Map.Entry<Integer, String>> map3 = map.entrySet();
Stream<Map.Entry<Integer, String>> stream5 = map3.stream();

​ 4.根据数组获取流

1
2
3
// 根据数组获取流
String[] arr = {"张颜宇","张三","李四","赵五","刘六","王七"};
Stream<String> stream6 = Stream.of(arr);

Stream流的常用方法

终结方法:返回值类型不再是Stream接口本身类型的方法,例如:forEach方法和count方法

非终结方法/延迟方法:返回值类型仍然是Stream接口自身类型的方法,除了终结方法都是延迟方法。例如:filter,limit,skip,map,conat

方法名称 方法作用 方法种类 是否支持链式调用
count 统计个数 终结方法
forEach 逐一处理 终结方法
filter 过滤 函数拼接
limit 取用前几个 函数拼接
skip 跳过前几个 函数拼接
map 映射 函数拼接
concat 组合 函数拼接

方法演示

1.count方法:long count (); 统计流中的元素,返回long类型数据

1
2
3
4
5
6
7
8
9
10
11
List<String> list = new ArrayList<>();
list.add("张老三");
list.add("张小三");
list.add("李四");
list.add("赵五");
list.add("张六");
list.add("王八");
long count = list.stream().count();
System.out.println("集合中的元素个数是:" + count);
输出结果:
集合中的元素个数是:6

2.filter方法

Stream filter(Predicate predicate); 过滤出满足条件的元素

参数Predicate:函数式接口,抽象方法:boolean test (T t)

Predicate接口:是一个判断接口

    // 获取stream流
    Stream<String> stream = Stream.of("张老三", "张小三", "李四", "赵五", "刘六", "王七");
    // 需求:过去出姓张的元素
    stream.filter((String name)->{
        return name.startsWith("张");
    }).forEach((String name)->{
        System.out.println("流中的元素" + name);
    });          

(上面引入Stream流时,就用到了这个方法)

3.forEach方法

void forEach(Consumer<? super T> action):逐一处理流中的元素
参数 Consumer<? super T> action:函数式接口,只有一个抽象方法:void accept(T t);

注意:
1.此方法并不保证元素的逐一消费动作在流中是有序进行的(元素可能丢失)
2.Consumer是一个消费接口(可以获取流中的元素进行遍历操作,输出出去),可以使用Lambda表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
List<String> list = new ArrayList<>();
list.add("张老三");
list.add("张小三");
list.add("李四");
list.add("赵五");
list.add("张六");
list.add("王八");

// 函数模型:获取流 --> 注意消费流中的元素
list.stream().forEach((String name)->{
System.out.println(name);
});

输出结果:
张老三
张小三
李四
赵五
张六

4.limit方法

Stream limit(long maxSize); 取用前几个元素

注意:参数是一个long 类型,如果流的长度大于参数,则进行截取;否则不进行操作

1
2
3
4
5
6
7
8
9
10
// 获取流的长度
Stream<String> stream1 = Stream.of("张老三", "张小三", "李四", "赵五", "刘六", "王七");
// 需求:保留前三个元素
stream1.limit(3).forEach((String name)->{
System.out.println("流中的前三个元素是:" + name);
});D
输出结果:
流中的前三个元素是:张老三
流中的前三个元素是:张小三
流中的前三个元素是:李四

5.map方法

Stream map(Function<? super T,? exception R> mapper;
参数Function<T,R>:函数式接口,抽象方法:R apply(T t);
Function<T,R>:其实就是一个类型转换接口(T和R的类型可以一致,也可以不一致)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取Stream流
Stream<String> stream1 = Stream.of("11","22","33","44","55");
// 需求:把stream1流中的元素转换为int类型
stream1.map((String s)->{
return Integer.parseInt(s); //String类型的s进行转换为Integer类型的元素,并返回
}).forEach((Integer i)->{
System.out.println(i); // 将转换后的int类型的元素逐一输出
});
输出结果:
11
22
33
44
55

6.skip方法

Stream skip(long n); 跳过前几个元素
注意: 如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空流

1
2
3
4
5
6
7
8
9
10
11
// 获取stream流
Stream<String> stream = Stream.of("张老三", "张小三", "李四", "赵五", "刘六", "王七");

stream.skip(3).forEach((String name)->{
System.out.println("跳过前三个,打印剩下的" + name);
});

输出结果:
跳过前三个,打印剩下的赵五
跳过前三个,打印剩下的刘六
跳过前三个,打印剩下的王七

7.concat方法

public static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
–> 合并两个流

1
2
3
4
5
6
7
8
Stream<String> stream1 = Stream.of("11","22","33","44","55");
Stream<String> stream2 = Stream.of("张颜宇", "张三", "李四", "赵五", "刘六", "王七");

// 需求:合并两个流
Stream<String> stream = Stream.concat(stream1,stream2);
stream.forEach((String name)->{
System.out.print(name);
输出结果:1122334455张颜宇张三李四赵五刘六王七

收集Stream流

<R, A> R collect(Collector<? super T, A, R> collector); 把流中的数据收集到单列集合中
返回值类型是R。R指定为什么类型,就是手机到什么类型的集合
参数Collector<? super T, A, R>中的R类型,决定把流中的元素收集到哪个集合中
参数Collector如何得到 ?,可以使用 java.util.stream.Collectors工具类中的静态方法:

                    - public static <T> Collector<T, ?, List<T>> toList():转换为List集合
                                            - public static <T> Collector<T, ?, Set<T>> toSet() :转换为Set集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

List<String> list2 = new ArrayList<>();
list2.add("张老三");
list2.add("张小三");
list2.add("李四");
list2.add("赵五");
list2.add("张六");
list2.add("王八");
// 需求:过滤出姓张的并且长度为3的元素

Stream<String> stream = list2.stream().filter((String name) -> {
return name.startsWith("张");
}).filter((String name) -> {
return name.length() == 3;
});

// stream 收集到单列集合中
List<String> list = stream.collect(Collectors.toList());
System.out.println(list);

// stream 手机到单列集合中
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set);

Java8新特性
http://example.com/2023/04/26/Java8新特性/
Author
Posted on
April 26, 2023
Licensed under