> 文章列表 > 函数式编程、Stream流操作复习

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

函数式编程、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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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>() {@Overridepublic boolean test(Book book) {return book.getPrice() < 90;}}).forEach(new Consumer<Book>() {@Overridepublic 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;@Overridepublic 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;       }elseresult = 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()));
// 成功输出   kkOptional<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);
// trueOptional<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>() {@Overridepublic 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  操作