1. Lambda表达式
1.1 相关说明
-
创建函数式接口的匿名对象时,可以使用Lambda表达式来减少代码量
-
语法格式:(参数列表) -> {代码体}
-
函数式接口:只有一个抽象方法的接口,可以使用注解@FunctionalInterface来标记对应接口
函数式接口是“面向方法编程”的思想。当某个方法的参数是一个函数式接口时,那么传进一个该接口的实现类,其实就是把这个方法本身作为传参,接参的地方就会执行这个方法的逻辑。
-
Java内置的4个基本函数式接口如下
接口名 方法 Consumer<T> void accept(T t) Supplier<T> T get() Function<T, R> R apply(T t) Predicate<T> boolean test(T t)
1.2 例子
1.2.1 Consumer<T>
public class Test {
public static void main(String[] args) {
addMoney(400, money -> System.out.println("零花钱加了"+ money + "元"));
}
public static void addMoney(double money, Consumer<Double> consumer) {
consumer.accept(money);
}
}
1.2.2 Predicate<T>
public class Test {
public static void main(String[] args) {
List<String> list = Arrays.asList("123abc", "456", "4abc");
// Lambda表达式中,代码体只有一行时,可以省略花括号和"return"
List<String> result = filterString(list, str -> str.contains("abc"));
System.out.println(result);
}
public static List<String> filterString(List<String> list, Predicate<String> pre) {
ArrayList<String> result = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
result.add(s);
}
}
return result;
}
}
2. 方法引用
2.1 相关说明
-
本质上还是Lambda表达式,对应的接口也得是函数式接口,代码量更少
-
对于Lambda表达式代码体中的方法,已有其他现成的方法时,可以使用方法引用
-
语法格式
1、对象::非静态方法
2、类::静态方法
3、类::非静态方法
2.2 例子
2.2.1 对象::非静态方法
与"类::静态方法"的情况类似
public class Test {
public static void main(String[] args) {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("Lambda表达式");
// 上述Lambda表达式中代码体的效果与PrintStream的println(T t)方法一致
// 且参数列表也一样,于是可以用方法引用
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("方法引用");
}
}
2.2.2 类::非静态方法
public class Test {
public static void main(String[] args) {
// 类::非静态方法
// Comparator的int compare(T t1, T t2)
// String的int t1.compareTo(t2)
Comparator<String> con = String::compareTo;
// 实际上是调用了"abc".compareTo("abq")
System.out.println(con.compare("abc", "abq"));
}
}
3. 构造器引用
public class Test {
public static void main(String[] args) {
// 原始写法
Function<String, Student> fun1 = new Function<String, Student>() {
@Override
public Student apply(String name) {
// 自定义Student类,构造器传入学生名字
return new Student(name);
}
};
// Lambda表达式
Function<String, Student> fun2 = name -> new Student(name);
// 构造器引用,在Student类中刚好有需要一个参数的构造器,所以能直接用Student::new
Function<String, Student> fun3 = Student::new;
Student tom = fun3.apply("Tom");
System.out.println(tom);
}
}
4. Stream API
4.1 相关说明
-
Stream操作有点类似于复杂的SQL语句,可以对集合数据进行过滤、截取等操作
-
Stream关注的是对数据的运算,与CPU打交道;集合关注的是数据的存储,与内存打交道
-
Stream本身不会存储元素
-
Stream不会改变源对象,会返回一个新的Stream
-
Stream操作是延迟执行的,只有执行终止操作时才执行中间操作
-
执行流程如下
1、实例化
2、中间操作(过滤、映射等)
3、终止
-
一旦执行终止操作,Stream将不能再使用;想要再用,就得再实例化
4.2 操作
4.2.1 实例化
- 通过集合的stream()方法获取顺序流;parallelStream()方法获取并行流
- 通过Arrays.stream()方法,参数是数组
- 通过Stream.of()方法,参数是多个元素,如:Stream.of(1, 2, 3, 4)
4.2.2 筛选与切片
@Test
public void test() {
List<String> list = Arrays.asList("123abc", "456", "789abc", "456");
// filter():过滤,过滤包含"abc"的
// forEach():这是一个终止操作,参数传一个Consumer
list.stream().filter(str -> str.contains("abc")).forEach(System.out::println);
// limit():截取
list.stream().limit(2).forEach(System.out::println);
// skip():跳过多个元素
list.stream().skip(1).forEach(System.out::println);
// distinct():去重
list.stream().distinct().forEach(System.out::println);
}
4.2.3 映射
1、map()
@Test
public void test() {
List<String> list = Arrays.asList("aa", "bb", "cc");
// map():映射,参数是Function类
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
}
2、flatMap()
public class StreamTest {
@Test
public void test() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
// 如果使用map(),得到的Stream中的元素是Stream,不会把自定义方法的返回值拆开
Stream<Stream<Character>> streamStream = list.stream().map(StreamTest::fromStringToStream);
streamStream.forEach(s -> s.forEach(System.out::print));
// 如果使用flatMap(),得到的Stream中的元素是Character,会把自定义方法的返回值拆开
Stream<Character> characterStream = list.stream().flatMap(StreamTest::fromStringToStream);
characterStream.forEach(System.out::print);
}
// 自定义方法,把字符串的每个字符加到list集合中,返回该list的Stream
public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}
4.2.4 排序
@Test
public void test() {
List<Integer> list = Arrays.asList(88, -10, 40);
// sorted():排序,如果集合中的元素是自定义类,则要么sorted()参数传一个比较器,要么该自定义类实现Comparable接口
list.stream().sorted().forEach(System.out::println);
}
4.2.5 终止之查找与匹配
| 方法 | 说明 |
|---|---|
| boolean allMatch(Predicate p) | 检查是否匹配所有元素 |
| boolean anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
| boolean noneMatch(Predicate p) | 检查是否没有匹配元素 |
| Optional findFirst() | 获取第一个元素 |
| Optional findAny() | 获取任一元素 |
| long count() | 统计元素个数 |
| Optional max(Comparator c) | 获取最大值元素 |
| Optional min(Comparator c) | 获取最小值元素 |
| void forEach(Consumer c) | 内部迭代 |
@Test
public void test() {
// 自定义Student类的集合,Student类有name和age属性
List<Student> list = Arrays.asList(new Student("jack", 20), new Student("tom", 50), new Student("mi", 30));
Optional<Student> max = list.stream().max((e1, e2) -> e1.getAge() - e2.getAge());
System.out.println(max);
}
4.2.6 终止之归约
@Test
public void test() {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
// T reduce(T identity, BinaryOperator<T> accumulator)
// 第一个参数是初始值,
// 第二个参数表示,将流中的元素反复结合起来,结合的方式具体要看传入的参数,本例的意思是:
// 初始值10加上第一个元素1得到11,11加上第二个元素2得到13,如此循环
Integer reduce = list.stream().reduce(10, Integer::sum);
System.out.println(reduce);
}
@Test
public void test2() {
List<Student> list = Arrays.asList(new Student("jack", 20), new Student("tom", 50), new Student("mi", 30));
Stream<Integer> ageStream = list.stream().map(Student::getAge);
// Optional<T> reduce(BinaryOperator<T> accumulator)
// 只有一个参数,将流中的元素反复结合起来,结合的方式是依次累加
Optional<Integer> reduce = ageStream.reduce((d1, d2) -> d1 + d2);
System.out.println(reduce);
}
4.2.7 终止之收集
@Test
public void test() {
List<Student> list = Arrays.asList(new Student("jack", 20), new Student("tom", 50), new Student("mi", 30));
// collect(Collector c):收集
// 把结果收集到list中
List<Student> resultList = list.stream().filter(e -> e.getAge() > 20).collect(Collectors.toList());
resultList.forEach(System.out::println);
// 把结果收集到set中
Set<Student> resultSet = list.stream().filter(e -> e.getAge() > 20).collect(Collectors.toSet());
resultSet.forEach(System.out::println);
}
5. Optional类
在Stream操作中经常会返回这种类型。Optional<T>是一个容器类,存放T类型的值,可以避免空指针异常。
5.1 实例化
@Test
public void test() {
Student tom = new Student("tom", 20);
// 使用Optional.of()创建实例,参数不能是null
// Optional.ofNullable()的参数可以是null
Optional<Student> student = Optional.of(tom);
System.out.println(student);
}
5.2 相关方法
| 方法 | 说明 |
|---|---|
| Optional.of(T t) | 创建一个Optional实例,参数t不能为空 |
| Optional.empty() | 创建一个空的实例 |
| Optional.ofNullable(T t) | 创建一个Optional实例,参数t可以为空 |
| boolean isPresent() | 判断Optional的内容是否为空 |
| void ifPresent(Consumer c) | 如果内容不为空,则执行Consumer代码,并把Optional的内容作为参数传入Consumer中 |
| T get() | 返回Optional的内容,如果为空则抛异常 |
| T orElse(T t) | 如果当前Optional的内容非空,则返回该内容;否则如果为空,返回参数里的t |
| T orElseGet(Supplier other) | 返回Optional的内容,如果为空则返回Supplier代码中的返回值 |
| T orElseThrow(Supplier s) | 返回Optional的内容,如果为空则抛出Supplier代码中抛出的异常 |