Java8函数式编程
笔记参考视频
黑马Java基础教程,3小时Java-Stream流从入门到精通_哔哩哔哩_bilibili
一、Lambda 表达式
基本格式,怎么简单的理解这个东西。
() -> {}
然后就是我看了好几个视频案例,基本意思就是简化匿名内部类
private int calcNum(IntBinaryOperator binaryOperator){
int a = 20;
int b = 30;
return binaryOperator.applyAsInt(a,b);
}
@Test
public void testCalcNum(){
// int i = calcNum(new IntBinaryOperator() {
// @Override
// public int applyAsInt(int left, int right) {
// return left + right;
// }
// });
// System.out.println(i);
// int i = calcNum((a, b) -> a + b);
// System.out.println(i);
int i = calcNum(Integer::sum);
System.out.println(i);
}
二、Stream流
Java8的Stream使用的是函数式编程模式,如同它的名字一样,它可以被用来 对集合或数组 进行链状流式的操作。可以更方便的让我们对集合或数组操作。

2.1 Stream 流入门小案例
准备工作
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author {
private Long id;
private String name;
private Integer age;
private String intro;
private List<Book> books;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Book {
private Long id;
private String name;
private String category;
private Integer score;
private String intro;
}
public static List<Author> getAuthors(){
Author author1 = new Author(1L,"张三",18,"小说作家",null);
Author author2 = new Author(2L,"李四",15,"散文作家",null);
Author author3 = new Author(3L,"王五",89,"诗人",null);
Author author4 = new Author(3L,"王五",89,"诗人",null);
List<Book> books1 = new ArrayList<>();
List<Book> books2 = new ArrayList<>();
List<Book> books3 = new ArrayList<>();
books1.add(new Book(1L,"鲍尔是怎么炼成的","励志",90,"这是一段描述"));
books1.add(new Book(2L,"小矮人和七个公主","童话",90,"这是一段描述"));
books1.add(new Book(3L,"鲍尔是怎么炼成的","励志",90,"这是一段描述"));
books2.add(new Book(1L,"七律长征","诗词",90,"这是一段描述"));
books2.add(new Book(2L,"乌克兰战场形式","军事",90,"这是一段描述"));
books2.add(new Book(3L,"围城","小说",90,"这是一段描述"));
books3.add(new Book(1L,"亮剑","小说",90,"这是一段描述"));
books3.add(new Book(2L,"Java高级程序设计","技术",90,"这是一段描述"));
books3.add(new Book(3L,"JS犀牛书","技术",90,"这是一段描述"));
author1.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
author4.setBooks(books3);
return Arrays.asList(author1,author2,author3,author4);
}
使用Stream流,过滤出年龄小于18岁的作者
// 打印所有年龄小于18岁的作者
authors.stream()
.distinct()
.filter(author -> author.getAge() < 18)
.forEach(System.out::println);
IDEA中 Debug Stream流

每个版本都不一样,这里暂时记录一下
使用Stream流要有终结操作,否则前面的调用没有意义
2.2 创建流
2.2.1 单例集合使用Stream流
像 List、Set 这种的都可以成为单列集合,而像Map这种的就称之为双列集合
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.stream()
.distinct()
.filter(i -> i %2 == 0)
.forEach(System.out::println);
2.2.2 数组使用 Stream 流
int[] arr = {1,2,3,4,5,6,7,8,9};
Arrays.stream(arr)
.filter(i -> i%2 == 0)
.forEach(System.out::println);
2.2.3 双列集合使用 Stream 流
Map<String,String> map = new HashMap<>();
map.put("name","1");
map.put("age","2");
map.put("sex","3");
map.entrySet()
.stream()
.filter(entry -> entry.getKey().equals("name"))
.forEach(System.out::println);
2.2.4 一堆零散的数据
使用 Stream.of()方法
2.3 中间操作
2.3.1 filter
// 过滤
authors.stream()
.filter(author -> author.getName().length() > 2)
.forEach(System.out::println);
2.3.2 map
这里的 map、filter和 js 很像,所以你可以像理解JS那样理解这个东西,就像个语法糖吧
// 返回新的对象
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.map(author -> author.getAge())
.map(author -> author + 10)
.forEach(System.out::println);
2.3.3 distinct
distinct是根据重写的equals和hashcode方法进行去重的
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.distinct()
.forEach(System.out::println);
2.3.4 sorted
默认不传参的情况
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.distinct()
.sorted()
.forEach(System.out::println);
注意,默认不传参的情况,需要该类实现 Comparable 接口
传参
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o1.getAge() - o2.getAge())
.forEach(System.out::println);
2.3.5 limit
// 设置流的最大长度,相当于分页
// 获取前两个元素
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.limit(2)
.forEach(System.out::println);
2.3.6 skip
// 跳过n个元素
// 跳过第一个元素输出
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.skip(1)
.forEach(System.out::println);
2.3.7 flatMap
flatMap呢,暂时可以理解为,map加强版。看两个案例吧,后面想到有更好的表达方式再说
// 输出所有的书籍
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.forEach(System.out::println);
// 输出所有的分类
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.stream(book.getCategory().split(",")))
.forEach(System.out::println);
2.3.8 concat
2.4 终结操作
还是通过案例来演示
2.4.1 forEach
// 遍历所有的 Author
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.distinct()
.forEach(System.out::println);
2.4.2 count
// 查看这些作家一共有多少本书
List<Author> authors = AuthorServices.getAuthors();
long count = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.count();
System.out.println(count);
2.4.3 max or min
// 查看价格最高和最低的书
List<Author> authors = AuthorServices.getAuthors();
Optional<Book> max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.max((book1, book2) -> book1.getScore() - book2.getScore());
System.out.println("价格最高的书" + max.get());
List<Author> authors = AuthorServices.getAuthors();
Optional<Book> min = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.min((book1, book2) -> book1.getScore() - book2.getScore());
System.out.println("价格最低的书" + min.get());
2.4.4 collect
啥意思呢?就是说我们使用forEach虽然可以打印输出,但是没有返回值。比方说我们想拿到 authors 里的所有作者名字,并且存到 List 中,这时候就需要用到 collect 将其转换为 List。其他的 Set 和 Map 同理。
collect 转换成 List
List<Author> authors = AuthorServices.getAuthors();
List<String> names = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());
System.out.println(names);
collect 转换成 Set
List<Author> authors = AuthorServices.getAuthors();
Set<Book> books = authors.stream()
.flatMap(author -> author.getBooks().stream())
.collect(Collectors.toSet());
System.out.println(books);
collect 转换成 Map
List<Author> authors = AuthorServices.getAuthors();
Map<String, List<Book>> collect = authors.stream()
//.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
.collect(Collectors.toMap(Author::getName, Author::getBooks));
System.out.println(collect);
2.4.5 anyMatch
如果有任意一个匹配就返回 true,否则返回 false
// 只要有作者年龄大于 18 岁的就返回 true
List<Author> authors = AuthorServices.getAuthors();
boolean result = authors.stream()
.anyMatch(author -> author.getAge() > 18);
System.out.println(result);
2.4.7 noneMatch
所有都不匹配就返回 true,否则返回 false
// 看看所有作者的年龄是不是都小于20岁
List<Author> authors = AuthorServices.getAuthors();
boolean result = authors.stream()
.noneMatch(author -> author.getAge() < 20);
System.out.println(result);
2.4.6 allMatch
所有都匹配返回 true,否则返回 false
// 看看所有的作者年龄是不是都大于20
List<Author> authors = AuthorServices.getAuthors();
boolean result = authors.stream()
.anyMatch(author -> author.getAge() > 20);
System.out.println(result);
2.4.7 findAny
// 随机查找一位 age > 60 的作家
List<Author> authors = AuthorServices.getAuthors();
Optional<Author> any = authors.stream()
.filter(author -> author.getAge() > 60)
.findAny();
any.ifPresent(System.out::println);
2.4.8 findFirst
// 找第一个
List<Author> authors = AuthorServices.getAuthors();
Optional<Author> first = authors.stream()
.filter(author -> author.getAge() > 10)
.findFirst();
first.ifPresent(System.out::println);
2.4.9 reduce
归并,和 js 里的 reduce 作用差不多
// 年龄累加
List<Author> authors = AuthorServices.getAuthors();
Integer reduce = authors.stream()
.map(Author::getAge)
.reduce(0, Integer::sum);
System.out.println(reduce);
// 年龄最大值
Integer reduce1 = authors.stream()
.map(Author::getAge)
.reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
System.out.println(reduce1);
// 年龄最小值
Integer reduce2 = authors.stream()
.map(Author::getAge)
.reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
System.out.println(reduce2);
源码里还有reduce的另一种形式
T reduce(T identity, 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();
2.4.10 forEach
lists.stream()
.filter(name -> name.startsWith("张"))
.filter(name -> name.length() >= 3)
.forEach(System.out::println);
2.4.11 toArray
String[] array = lists.stream().toArray(value -> new String[value]);
System.out.println(Arrays.toString(array));
三、Optional
3.1 创建对象
Optional<T> 类是Java8中引入的新类,它是用来表示一个值存在或者不存在的容器。
// 普通写法
if (getAuthor()!= null) {
System.out.println(getAuthor().getName());
}
// optional写法
// ofNullable 当 author 为null时不会报错
Optional<Author> author = Optional.ofNullable(getAuthor());
author.ifPresent(System.out::println);
改造 getAuthor函数,直接返回 Optional对象
public static Optional<Author> getAuthorOptional() {
Author author = new Author(1L, "成吉思汗", 18, "成吉思汗是中国历史上第一个出生于中国的满清皇帝。他是中国历史上第一个出身于满清皇权的皇帝,也是中国历史上第一个出身于满清皇权的皇后。", null);
return Optional.ofNullable(author);
}
// 测试
getAuthorOptional().ifPresent(System.out::println);
使用 Optional.of方法,会存在空指针异常的问题
所以最后总结,更推荐使用 Optional.ofNullable方法
3.2 安全获取值
orElse
orElse方法接受一个默认值作为参数,并在Optional对象为空时返回该默认值
无论 Optional 对象是否为空,orElse 方法都会执行默认值的计算,看代码示例,这里不太好演示,debug一下就能看到
Author author = getAuthorOptional().orElse(new Author(2L, "张三", 20, "xxx", null));
System.out.println(author);
orElseGet
oeElseGet方法接受一个Supplier函数式接口作为参数,并在 Optional 对象为空时调用该函数式接口获取默认值
只有在Optional对象为空时,orElseGet方法才会执行默认的计算
Author author = getAuthorOptional().orElseGet(() ->
new Author(2L, "张三", 20, "xxx", null));
System.out.println(author);
orElseThrow
如果Optional对象为空,则抛出异常
Author author = getAuthorOptional().orElseThrow(() -> new RuntimeException("作者不存在"));
3.3 过滤操作
filter
getAuthorOptional().filter(a -> a.getAge() > 18).ifPresent(System.out::println);
3.4 数据转换
map
// 这里就不演示了,和 stream 流的 map 用法差不多
四、函数式接口
只有一个抽象方法的接口称之为函数式接口
4.1 常见的函数式接口
Predicate
4.2 函数式接口常用默认方法
and
并且
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 18;
}
}.and(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length() > 1;
}
}))
.forEach(System.out::println);
or
或者
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.distinct()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 18;
}
}.or(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length() > 2;
}
}))
.forEach(System.out::println);
negate
类似于取反
List<Author> authors = AuthorServices.getAuthors();
authors.stream()
.distinct()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 18;
}
}.negate())
.forEach(System.out::println);
函数式接口通常用于自定义方法
五、方法引用
5.1 引用某个类的静态方法
基本格式
类名::方法名
需要满足 方法体只有一行代码,并且是调用了某个类的静态方法,并且要把重写的抽象方法中所有的参数都按照顺序传入了该方法中
AuthorServices.getAuthors()
.stream()
.distinct()
.map(author -> author.getAge())
.map(age -> String.valueOf(age));
// 优化
AuthorServices.getAuthors()
.stream()
.distinct()
.map(Author::getAge)
.map(String::valueOf)
.forEach(System.out::println);
5.2 引用对象的实例方法

5.3 引用类的实例方法

5.4 构造器引用

六、高级用法
6.1 stream基本数据类型优化
略,用到再补充
6.2 并行流

Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
// parallel 将串行流转换为并行流
integerStream.
.peek(new Consumer<Integer>() {
@Override
public void accept(Integer num) {
System.out.println(num + "--" + Thread.currentThread().getName());
}
})
.map(num -> num%2)
.reduce(Integer::sum)
.get();