函数式编程、Stream流操作复习

Lambda表达式

使用

当一个接口中只含有一个抽象方法时,这个接口就叫做函数式接口

一般使用@FunctionalInterface注解来标识这个接口是一个函数式接口。

但不论有没有标识这个注解,只要接口中只有一个抽象方法,那么这个接口就是函数式接口

当一个方法的参数是一个函数式接口类型时,我们就可以利用Lambda表达式来替换匿名内部类。

例如:

有一个函数式接口

public interface Optionor {
int opt(int a, int b);
}

有一个方法,需要函数式接口的类型作为形参

public static int intOpt(Optionor op){
int a = 2, b = 3;
return op.opt(a,b);
}

一般实现可能会是匿名内部类

int res = intOpt(new Optionor() {
@Override
public int opt(int a, int b) {
return a * b;
}
});
System.out.println(res);

可以使用Lambda表达式来简化函数式接口类型的参数

Lambda表达式只关注接口中这个抽象方法的参数和方法体

int res = intOpt((int a,int b) -> {
return a * b;
});
System.out.println(res);

优化:

  • 参数列表中可以省略参数类型,Java可以自动推断参数类型
  • 当参数列表只有一个参数时,小括号可以省略
  • 当方法体中,只有一句代码时,大括号可以省略
  • 当方法体中只有一个return语句时,return也可以省略

最开始的匿名内部类是这样的

int res = intOpt(new Optionor() {
@Override
public int opt(int a, int b) {
return a * b;
}
});
System.out.println(res);

现在利用Lambda表达式,是这样的,大大简化了代码

int res = intOpt((a, b) -> a * b);
System.out.println(res);

再来看一个例子,有一个函数式接口

public interface BigOpt {
int opt(int x);
}

有一个以此接口为形参的方法

public static int intFun(BigOpt op){
int a = 2;
return op.opt(a);
}

匿名内部类实现

int res = intFun(new BigOpt() {
@Override
public int opt(int x) {
return x += 1;
}
});
System.out.println(res);

Lambda表达式实现

int res = intFun(x -> x += 1);
System.out.println(res);

总结

利用Lambda表达式来简化代码,只要关注函数式接口中这个方法的参数列表和方法体就好,别的不用关系

!!!!!!!!!!!!

如果感觉直接写Lambda表达式会困难,可以先写匿名内部类,然后通过调用IDEA的快捷键 Alt + Enter来将匿名内部类转换成Lambda表达式

Stream流操作

Stream流是用来简化对数组、集合的操作,能够让其中的元素像“流”一样被操作。

创建流对象

Stream流操作的第一步获取Steam对象。

前提的数据准备

这是自定义对象

public class Book {
private String bookName;
private String authorName;
private Integer price;
// getter ... setter
// constructor....
// equals....
}
单列集合获取Stream对象

通过直接在单列集合对象上调用stream()方法

// 准备数据
List<Book> books = new ArrayList<>();
books.add(new Book("Java并发编程","kk",88));
books.add(new Book("JVM规范","zhangsan",98));
books.add(new Book("深入了解操作系统","xxx",88));
// 获取Stream流对象
Stream<Book> stream = books.stream();
数组获取Stream对象

通过Arrays.stream()Stream.of()方法

// 准备数据
Book[] books = new Book[]{
new Book("Java并发编程","kk",88),
new Book("JVM规范","zhangsan",98),
new Book("深入了解操作系统","xxx",88)
};
// 获取Stream流对象
Stream<Book> bookStream = Arrays.stream(books);
// 或者
Stream<Book> stream = Stream.of(books);
双列集合获取Stream对象

对于HashMap这类的数据结构,如何获取Stream对象

通过先调用entrySet()方法,获取Set集合,Set中存放的是Entry,一个Entry中封装了一对key和value。

因为Set是一个单列集合,所以可以通过stream()方法获取Stream流对象,Stream流对象中的泛型是Entry。

// 准备数据
Map<String, Book> map = new HashMap<String, Book>();
map.put("1",new Book("Java并发编程","kk",88));
map.put("2",new Book("JVM规范","zhangsan",98));
map.put("3",new Book("深入了解操作系统","xxx",88));
// 获取Stream流对象
Set<Map.Entry<String, Book>> entries = map.entrySet();
Stream<Map.Entry<String, Book>> stream = entries.stream();

流的常用中间操作

获取到Stream流对象后,就可来操作流中的数据了。

对Stream流的操作分为 中间操作 + 终结操作

终结操作就是把一个流中的最终的元素收集起来,例如forEach()、collect()方法就是终结操作。

流的操作的最后必须是一个终结操作,否则中间操作不生效。

先来看中间操作,这些中间操作的参数全是函数式接口,我们都可以利用Lambda表达式来完成

filter()

filter()方法用来筛选流中符合条件的元素,符合条件则保留在流中,不符合条件的元素则从流中剔除出去

过滤的意思,将流中的元素一个一个地传递给此方法,返回true就保留此元素,否则遗弃该元素。

此方法的形参是一个函数式接口,可以使用Lambda表达式来简化。

例如:筛选集合中价格小于90 的图书

这是数据

List<Book> books = new ArrayList<>();
books.add(new Book("Java并发编程", "kk", 88));
books.add(new Book("JVM规范", "zhangsan", 98));
books.add(new Book("深入了解操作系统", "xxx", 88));

使用匿名内部类是这样的

books.stream()
.filter(new Predicate<Book>() {
@Override
public boolean test(Book book) {
return book.getPrice() < 90;
}
})
.forEach(new Consumer<Book>() {
@Override
public void accept(Book book) {
System.out.println(book);
}
});
// 输出结果
// Book(bookName=Java并发编程, authorName=kk, price=88)
// Book(bookName=深入了解操作系统, authorName=xxx, price=88)

使用Lambda表达式

books.stream()
.filter(book -> book.getPrice() < 90)
.forEach(book -> System.out.println(book));
// 输出结果
// Book(bookName=Java并发编程, authorName=kk, price=88)
// Book(bookName=深入了解操作系统, authorName=xxx, price=88)
map()

对流中元素进行数据类型转换的操作

还是上面的数据,要求是显示价格小于90元的图书名称。

books.stream()
.filter(book -> book.getPrice() < 90)
.map(book -> book.getBookName())
.forEach(book -> System.out.println(book));
//Java并发编程
//深入了解操作系统

经过map()后,Stream流中的泛型就变成了我们想要的String类型,也就是只包含书籍名称。

distinct()

流中元素的去重操作,要求流中的元素必须重写了equals()方法,否则无法比对。

例如:去除重复的数据

// 准备数据
List<Book> books = new ArrayList<>();
books.add(new Book("Java并发编程", "kk", 88));
books.add(new Book("JVM规范", "zhangsan", 98));
books.add(new Book("深入了解操作系统", "xxx", 88));
books.add(new Book("深入了解操作系统", "xxx", 88));
// 去重操作
books.stream()
.distinct()
.forEach(book -> System.out.println(book));
// Book(bookName=Java并发编程, authorName=kk, price=88)
// Book(bookName=JVM规范, authorName=zhangsan, price=98)
// Book(bookName=深入了解操作系统, authorName=xxx, price=88)
sorted()

排序,流中元素的排序

要求:流中元素实现Compara接口或传入一个自定义的比较器

例如:要求按照书籍价格降序排列

  1. 流中元素实现Comparable接口
public class Book implements Comparable<Book>{
private String bookName;
private String authorName;
private Integer price;
@Override
public int compareTo(Book o) {
return this.getPrice() - o.getPrice();
}
}

这是数据

List<Book> books = new ArrayList<>();
books.add(new Book("Java并发编程", "kk", 78));
books.add(new Book("JVM规范", "zhangsan", 98));
books.add(new Book("深入了解操作系统", "xxx", 88));

利用Stream流中sorted()进行排序

books.stream()
.sorted()
.forEach(book -> System.out.println(book));
// Book(bookName=Java并发编程, authorName=kk, price=78)
// Book(bookName=深入了解操作系统, authorName=xxx, price=88)
// Book(bookName=JVM规范, authorName=zhangsan, price=98)
  1. 如果流中的元素没有实现Comparable接口,则可以传入一个比较器
books.stream()
.sorted((o1, o2) -> o1.getPrice() - o2.getPrice())
.forEach(book -> System.out.println(book));
// Book(bookName=Java并发编程, authorName=kk, price=78)
// Book(bookName=深入了解操作系统, authorName=xxx, price=88)
// Book(bookName=JVM规范, authorName=zhangsan, price=98)
limit()

对流中元素的范围进行截取,不在范围内的元素直接遗弃

例如:截取前2个元素

books.stream()
.limit(2)
.forEach(book -> System.out.println(book));
// Book(bookName=Java并发编程, authorName=kk, price=78)
// Book(bookName=JVM规范, authorName=zhangsan, price=98)
skip()

跳过,跳过流中前n个元素,即将指定范围内的元素遗弃

例如:除了第一本书之外的书按照价格排序

books.stream()
.skip(1)
.sorted((o1, o2) -> o1.getPrice() - o2.getPrice())
.forEach(book -> System.out.println(book));
//Book(bookName=深入了解操作系统, authorName=xxx, price=88)
//Book(bookName=JVM规范, authorName=zhangsan, price=98)
flatMap()

将元素中的可以转换为Stream流对象的字段或属性并入到流中

例如,一个作者可以出版很多本书

作者的实体类

public class Author {
private String name;
private Integer age;
private List<Book> bookList;
// getter... setter... constructor...
}

准备数据:一些作者以及他们的书籍

ArrayList<Author> authors = new ArrayList<>();
List<Book> bookList1 = new ArrayList<>();
bookList1.add(new Book("Vue.js前端框架","尤雨溪",39));
bookList1.add(new Book("Vite快速构建工具","尤雨溪",59));
bookList1.add(new Book("Vue3详解","尤雨溪",68));
authors.add(new Author("尤雨溪",30,bookList1));
List<Book> bookList2 = new ArrayList<>();
bookList2.add(new Book("丑男逆袭之路","kk",5));
bookList2.add(new Book("厕所暗藏玄机","kk",15));
bookList2.add(new Book("深入理解王者荣耀","kk",25));
authors.add(new Author("kk",21,bookList2));
ArrayList<Book> bookList3 = new ArrayList<>();
bookList3.add(new Book("我与地坛","史铁生",58));
bookList3.add(new Book("老屋小记","史铁生",38));
bookList3.add(new Book("病隙碎笔","史铁生",128));
authors.add(new Author("史铁生",59,bookList3));

例如:将年龄大于25岁的作者的所有的书籍按照价格排序输出

authors.stream()
.filter(author -> author.getAge() > 25)
.flatMap((Function<Author, Stream<Book>>) author -> author.getBookList().stream())
.sorted((o1, o2) -> o1.getPrice() - o2.getPrice())
.forEach(book -> System.out.println(book));
// Book(bookName=老屋小记, authorName=史铁生, price=38)
// Book(bookName=Vue.js前端框架, authorName=尤雨溪, price=39)
// Book(bookName=我与地坛, authorName=史铁生, price=58)
// Book(bookName=Vite快速构建工具, authorName=尤雨溪, price=59)
// Book(bookName=Vue3详解, authorName=尤雨溪, price=68)
// Book(bookName=病隙碎笔, authorName=史铁生, price=128)

当调用flatMap()之后,新的流中的元素泛型就变成了Book。

终结操作

流操作最后必须是一个终结操作,否则这一系列流操作都将无效。

流的终结操作也就是把流中的元素重新收集起来

forEach

遍历流中的元素

还是上面的那些测试数据

ArrayList<Author> authors = new ArrayList<>();
List<Book> bookList1 = new ArrayList<>();
bookList1.add(new Book("Vue.js前端框架","尤雨溪",39));
bookList1.add(new Book("Vite快速构建工具","尤雨溪",59));
bookList1.add(new Book("Vue3详解","尤雨溪",68));
authors.add(new Author("尤雨溪",30,bookList1));
List<Book> bookList2 = new ArrayList<>();
bookList2.add(new Book("丑男逆袭之路","kk",5));
bookList2.add(new Book("厕所暗藏玄机","kk",15));
bookList2.add(new Book("深入理解王者荣耀","kk",25));
authors.add(new Author("kk",21,bookList2));
ArrayList<Book> bookList3 = new ArrayList<>();
bookList3.add(new Book("我与地坛","史铁生",58));
bookList3.add(new Book("老屋小记","史铁生",38));
bookList3.add(new Book("病隙碎笔","史铁生",128));
authors.add(new Author("史铁生",59,bookList3));

例如:打印作者名称叫做"kk"的所有书籍的名称

authors.stream()
.filter(author -> author.getName().equals("kk"))
.flatMap((Function<Author, Stream<Book>>) author -> author.getBookList().stream())
.forEach(book -> System.out.println(book.getBookName()));
// 丑男逆袭之路
// 厕所暗藏玄机
// 深入理解王者荣耀
count()

统计流中元素的数量

统计所有作者的书籍的数量,注意去重

long count = authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBookList().stream())
.distinct()
.count();
System.out.println(count);
// 9
min() 和max()

求出流中的最值

这两个方法的返回值是Optional类型,里面封装了目标数据,关于Optional类型的用法,看后面。

选出所有作者的书籍的价格最便宜的书

Optional<Book> min = authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBookList().stream())
.distinct()
.min((o1, o2) -> o1.getPrice() - o2.getPrice());
Book book = min.get();
System.out.println(book);
// Book(bookName=丑男逆袭之路, authorName=kk, price=5)
collect()

将流中的元素重新收集成集合

可以转换为List、Set、Map类型的集合

  1. 转换为List集合

直接传入工具类Collectors的静态方法,Collectors.toList()方法的返回值就是一个转换器。

所有作者的姓名抽取出来为List

List<String> nameList = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());
System.out.println(nameList);
// [尤雨溪, kk, 史铁生]
  1. 转换为Set集合

同样传入Collectors工具类的静态方法即可,该方法的返回值就是一个转换器。

Set<String> nameList = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toSet());
System.out.println(nameList);
// [尤雨溪, kk, 史铁生]
  1. 转换为Map集合

同样是调用Collectors的toMap()方法,但是此方法需要两个函数式接口类型的参数,用来指定哪一个作为key,哪一个作为value

Map集合的key为作者姓名,value为图书列表

List<Author> authors = getAuthorList();
Map<String, List<Book>> map = authors.stream()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBookList()));
System.out.println(map);
// {
// kk=[
// Book(bookName=丑男逆袭之路, authorName=kk, price=5),
// Book(bookName=厕所暗藏玄机, authorName=kk, price=15),
// Book(bookName=深入理解王者荣耀, authorName=kk, price=25)
// ],
// 尤雨溪=[
// Book(bookName=Vue.js前端框架, authorName=尤雨溪, price=39),
// Book(bookName=Vite快速构建工具, authorName=尤雨溪, price=59),
// Book(bookName=Vue3详解, authorName=尤雨溪, price=68)
// ],
// 史铁生=[
// Book(bookName=我与地坛, authorName=史铁生, price=58),
// Book(bookName=老屋小记, authorName=史铁生, price=38),
// Book(bookName=病隙碎笔, authorName=史铁生, price=128)
// ]
// }
查找和匹配
anyMatch()

只要流有任意一个元素符合条件,则返回true,如果所有的元素都不符合条件,则返回false。

例如:是否有年龄大于50的作者

boolean b = authors.stream()
.anyMatch(author -> author.getAge() > 50);
System.out.println(b);
// true
allMatch()

所有的元素都匹配条件,才会返回true。

例如:判断所有的作者是否都大于30岁

boolean b = authors.stream()
.allMatch(author -> author.getAge() > 30);
System.out.println(b);
// false
noneMatch()

判断流中所有的元素是否都不满足匹配条件。

allMatch()也能实现此方法

例如:判断所有的作者都没有超过100岁

boolean b = authors.stream()
.noneMatch(author -> author.getAge() >= 100);
System.out.println(b);
// true
findAny()

获取流中的任意一个元素,没法保证是第一个元素。

在流中随机获取一个元素,该方法的返回值也是一个Optional,关于Optional后面详细说。

例如:返回一个年龄大于18的作者

Optional<Author> any = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();
System.out.println(any.get());
// Author(name=尤雨溪, age=30, bookList=...
findFirst()

返回流中的第一个元素

例如: 获取年龄最小的作者的姓名

Optional<Author> first = authors.stream()
.sorted((o1, o2) -> o1.getAge() - o2.getAge())
.findFirst();
System.out.println(first.get());
// Author(name=kk, age=21, bookList=....
reduce

reduce: 归纳、减少、归并

对流中所有的元素按照指定方式计算,返回一个结果。

对流中所有的元素组合起来,可以传入初始值

reduce()方法有三种重载方式,

  1. 两个参数的重载形式:其中第一个参数是初始值,第二个参数是比较器
T reduce(T identity, BinaryOperator<T> accumulator);

底层原理是这样的

T result = identity;
for(T el : stream){
result = accumulator.apply(result, el);
}
return result;

求年龄的最小值

Integer min = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MAX_VALUE, (res, integer) -> res < integer ? res : integer);
System.out.println(min);
// 21
  1. 一个参数的重载形式:参数就是一个比较器
Optional<T> reduce(BinaryOperator<T> accumulator);

底层是这样的,会将流中的第一个元素作为初始值

boolean foundAny = false;
T result = null;
for (T element : this stream) {
if (!foundAny) {
foundAny = true;
result = element;
}else
result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();

例如:使用reduce()求所有作者年龄的和

Optional<Integer> res = authors.stream()
.map(author -> author.getAge())
.reduce((integer, integer2) -> integer + integer2);
System.out.println(res.get());
// 110

例如:使用reduce()求年龄最大的作者

Optional<Author> op = authors.stream()
.reduce((author, author2) -> author.getAge() > author2.getAge() ? author : author2);
System.out.println(op.get());
// Author(name=史铁生, age=59, bookList=.....
  1. 第三种重载形式需要配合并行流使用,后面说
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);

对基本数据类型的操作

当Stream流中的元素是基本数据类型时,在操作时就会发生自动装箱、自动拆箱的操作,如果数据量大了,这就造成了不必要的资源损耗。

因此Stream中提供了对基本数据类型操作的方法,用来批量对基本数据类型进行操作,例如

  • mapToInt()
  • mapToLong()
  • mapToDouble()
  • flatToInt()
  • flatToLong()

注意事项

  • 惰性求值:一系列流的操作,如果没有终结操作,中间操作是不会执行的
  • 流是一次性:一个流对象一旦经过一个终结操作,这个流对象已经不能再使用了
  • 不会影响原始集合或数组中的数据
Stream<Author> stream = authors.stream();
// 使用流对象
stream.forEach(author -> System.out.println(author));
// 继续使用该流对象
// 报错,因为该流对象已经被关闭,已经不可使用了
stream.forEach(author -> System.out.println(author));

Optional对象

概述

编写代码时,需要做非空判断,否则就会出现NullPointerException。

如果在代码中有大量的非空判断,整个代码就会非常臃肿。

JDK8提供了一个Optional类,正确使用Optional对象,能够避免空指针异常。

在很多函数式编程的方法的返回值就是一个Optional。

使用

Optional好像一个封装类,其内部封装了我们的目标类型的数据,将目标数据封装到Optionnal对象的value属性上

创建Optional对象
  1. 常用的用来创建Optional对象的方法是Optional.ofNullable()方法,此方法会返回一个Optional对象。
Author author = new Author("kk", 21,null);
Optional<Author> op = Optional.ofNullable(author);
op.ifPresent(author1 -> System.out.println(author1.getName()));
// kk
  1. 还有一个静态方法Optional.of()方法,前提是必须保证目标数据不为null,否则报错无法创建。
Author author = new Author("kk", 21,null);
// 创建 成功
Optional<Author> op = Optional.of(author);
// 报错,创建失败
Optional<Author> opp = Optional.of(null);
  1. 如果想将null封装到Optional对象中,可以调用Optional.empty()方法,知道就好。
安全消费目标数据
ifPresent()

通过调用Optional对象的ifPresent()方法来消费目标数据,需要传入一个消费类型的接口。

如果目标数据为null,则不会执行消费方法

Author author = new Author("kk", 21,null);
Optional<Author> op = Optional.ofNullable(author);
op.ifPresent(author1 -> System.out.println(author1.getName()));
// 成功输出 kk
Optional<Author> opp = Optional.ofNullable(null);
opp.ifPresent(author12 -> System.out.println(author12.getName()));
// 不会执行消费的方法
获取值

如何直接获取到目标数据,即直接拿到Optional对象的value属性值

get()

返回目标数据,如果目标数据为null(即Optional对象的value为null),则会报错

Optional<Author> op = Optional.ofNullable(author);
Author aa = op.get();
System.out.println(aa);
// 成功
// 目标数据为null
Optional<Author> op = Optional.ofNullable(null);
// 报错
Author aa = op.get();

如果希望安全获取值,则不推荐使用get()方法,可以使用以下两种方法

orElseGet()

该方法的参数是一个函数式接口。

如果目标数据不为null,则直接返回。

如果为null,则调用自定义的方法来返回一个默认值

Author author = new Author("kk", 21,null);
Optional<Author> op = Optional.ofNullable(author);
Author aut = op.orElseGet(() -> new Author("lmk", 21, null));
System.out.println(aut);
// Author(name=kk, age=21, bookList=null)
// 目标数据为null
Optional<Author> opp = Optional.ofNullable(null);
Author a = opp.orElseGet(() -> new Author("lmk", 21, null));
System.out.println(a);
// Author(name=lmk, age=21, bookList=null)
orElseThrow()

如果目标数据不为空则返回目标数据。

如果目标数据为null,则抛出我们自定义的异常

该方法的参数仍然是一个函数式接口

因为该方法会抛出异常,所以在调用该方法时,需要做异常处理

Author author = new Author("kk", 21,null);
Optional<Author> op = Optional.ofNullable(author);
try {
Author aa = op.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("亲~~目标数据为null,不可以获取呦~~"));
System.out.println(aa);
// Author(name=kk, age=21, bookList=null)
} catch (Throwable e) {
e.printStackTrace();
}

抛出异常

Author author = new Author("kk", 21,null);
Optional<Author> op = Optional.ofNullable(author);
try {
Author aa = op.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("亲~~目标数据为null,不可以获取呦~~"));
System.out.println(aa);
// Author(name=kk, age=21, bookList=null)
} catch (Throwable e) {
e.printStackTrace();
}
// java.lang.RuntimeException: 亲~~目标数据为null,不可以获取呦~~
过滤

在Optional对象中同样有一个filter()方法,会对目标数据进行过滤。

如果原本的目标数据不符合条件,则会返回value为null的Optional对象

Author author = new Author("kk", 21,null);
Optional<Author> op = Optional.ofNullable(author);
// 过滤
Optional<Author> opp = op.filter(author1 -> author1.getAge() > 30);
// 消费新的数据
opp.ifPresent(author12 -> System.out.println(author12.getAge()));
// 无输出
判断

判断当前Optional中的数据是否为空,通过调用Optional对象的isPresent()方法

Optional<Author> op = Optional.ofNullable(author);
boolean b = op.isPresent();
System.out.println(b);
// true
Optional<Author> op = Optional.ofNullable(null);
boolean b = op.isPresent();
System.out.println(b);
// false
数据转换

Optional中提供了一个方法map(),使用该方法来对Optional中的目标数据进行类型转换,返回值是封装了新数据类型的Optional对象。

Author author = new Author("kk", 21,null);
Optional<Author> op = Optional.ofNullable(author);
// 新的目标数据类型的Optional对象
Optional<String> sop = op.map(author1 -> author1.getName());
sop.ifPresent(s -> System.out.println(s));
// kk

方法引用

在使用Lambda表达式时,如果方法体中只有一个方法调用的话,可以直接使用方法引用来简化。

例如:用Lambda表达式

authors.stream()
.map(author -> author.getName())
.forEach(name -> System.out.println(name));

用方法引用

authors.stream()
.map(Author::getName)
.forEach(System.out::println);

基本格式

类名或对象名::方法名

当我们在书写Lambda表达式时,不用考虑什么时候用哪种方法引用,

我们只需要在写完Lambda表达式后,发现方法体只有一行代码,并且是方法的调用,此时就可以利用Alt + Enter来快速转换成方法引用

当方法引用的多了,就能直接写出方法引用

引用类的静态方法

引用类的静态方法

类名::方法名

要保证我们的Lambda表达式中的参数能够正确被此方法调用,即能够正确传递给此方法。

引用对象的实例方法

对象名::方法名

同时,也要保证Lambda表达式中的参数能够正确传递给此方法的参数列表。

Lambda表达式的参数,作为一个另一个对象的方法调用的参数

List<String> list = new ArrayList<>();
list.add("abc");
list.add("lmk");
list.add("xyz");
list.stream()
.forEach(str -> System.out.println(str));

ps:out是System类中的一个对象

List<String> list = new ArrayList<>();
list.add("abc");
list.add("lmk");
list.add("xyz");
list.stream()
.forEach(System.out::println);

引用类的实例方法

类名::方法名

保证:Lambda表达式的参数,在方法体中调用这个参数身上的方法

List<String> list = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());

类的实例方法引用

List<String> list = authors.stream()
.map(Author::getName)
.collect(Collectors.toList());

构造器引用

Lambda表达式的方法体只调用了构造方法

类名::new

要保证,Lambda表达式的参数能够正确作为构造方法的参数

用构造器引用优化下面的代码

List<StringBuffer> list = stringList.stream()
.map(new Function<String, StringBuffer>() {
@Override
public StringBuffer apply(String s) {
return new StringBuffer(s);
}
}).collect(Collectors.toList());

构造器引用后

List<StringBuffer> list = stringList.stream()
.map(StringBuffer::new).collect(Collectors.toList());

并行流

我们之前的流操作,都是串行流。

当流中有大量的元素时,我们可以使用并行流来提供操作的效率。

并行流就是把任务分配给多个线程去完成,并行流的操作都是封装好了的,我们只需要调用就好。

并行流在数据量很大时,执行效率才会高,当数据量很少时,使用并行流反而增大了资源消耗。

我们只需要在流操作前调用parallel()方法,此时的流对象就会变成一个并行流,接下来的流操作都是多线程处理。

List<String> list = new ArrayList<>();
list.add("abc");
list.add("lmk");
list.add("xyz");
list.stream()
.parallel()
.forEach(x -> System.out.println(x + "正在被" + Thread.currentThread().getName() + " 操作"));
// lmk正在被main 操作
// xyz正在被ForkJoinPool.commonPool-worker-9 操作
// abc正在被ForkJoinPool.commonPool-worker-2 操作
posted @   秋天Code  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示