函数式编程、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接口或传入一个自定义的比较器
例如:要求按照书籍价格降序排列
- 流中元素实现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)
- 如果流中的元素没有实现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类型的集合
- 转换为List集合
直接传入工具类Collectors的静态方法,Collectors.toList()方法的返回值就是一个转换器。
所有作者的姓名抽取出来为List
List<String> nameList = authors.stream() .map(author -> author.getName()) .collect(Collectors.toList()); System.out.println(nameList); // [尤雨溪, kk, 史铁生]
- 转换为Set集合
同样传入Collectors工具类的静态方法即可,该方法的返回值就是一个转换器。
Set<String> nameList = authors.stream() .map(author -> author.getName()) .collect(Collectors.toSet()); System.out.println(nameList); // [尤雨溪, kk, 史铁生]
- 转换为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()方法有三种重载方式,
- 两个参数的重载形式:其中第一个参数是初始值,第二个参数是比较器
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
- 一个参数的重载形式:参数就是一个比较器
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=.....
- 第三种重载形式需要配合并行流使用,后面说
<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对象
- 常用的用来创建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
- 还有一个静态方法
Optional.of()
方法,前提是必须保证目标数据不为null,否则报错无法创建。
Author author = new Author("kk", 21,null); // 创建 成功 Optional<Author> op = Optional.of(author); // 报错,创建失败 Optional<Author> opp = Optional.of(null);
- 如果想将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 操作
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现