含义
Lambda 表达式也叫作匿名函数,是一种是未绑定标识符的函数定义,在编程语言中,匿名函数通常被称为 Lambda 抽象
Lambda 表达式通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用。这种匿名函数,在 JDK 8 之前是通过 Java 的匿名内部类来实现,从 Java 8 开始则引入了 Lambda 表达式——一种紧凑的、传递行为的方式。
语法
参数 -> 具体实现
包含一个 Lambda 表达式的运算符 ->
,在运算符的左边是输入参数,右边则是函数主体。
省略规则
- 参数类型可以省略
- 方法体中只有一句代码时大括号return和唯一一句代码的分号可以省略
- 方法只有一个参数时小括号可以省略
Stream
流不会影响原数据(我们在流中可以处理很多数据,但是正常情况下是不会影响到原来集合中的元素的)
流代码分为三步:
1.创建流
单列集合:集合对象.stream()
List<Author> authors = getAuthors();
stream<Author> stream = authors.stream();
数组: Arrays.stream(数组)或者使用Stream.of 来创建
Integer[] arr = {1,2,3,4,5};
stream<Integer> stream = Arrays.stream(arr);
stream<Integer> stream2 = stream.of(arr);
双列集合:转换成单列集合后再创建
Map<String, Integer> map = new HashMap<>();
map.put("蜡笔小新", 19);
map.put("黑子", 17);
map.put("日向翔阳", 16);
Stream<Entry<String, Integer>> stream = map.entrySet().stream();
2.中间操作
中间操作之filter
可以对流中的元素进行**条件过滤
**,符合过滤条件的才能继续留在流中
// 打印所有结名长度大于1的作家的姓名
List<Author> authors = getAuthors();
authors.stream()
.filter(author -> author.getName().length() > 1)
.forEach(author -> System.out.println(author.getName()));
中间操作之map
map()方法中的第一个泛型不能改(第一个类型就是对象的元素,已经是定义好的,不能修改不然会报错),但是第二个泛型是可以修改的(可以修改为想要的数据类型),简单理解为就是把流当中的元素转换为另外一种元素类型然后再放到流中
mapToInt() 高级用法.....还有mapTOLong()等等,针对基本数据类型进行优化。
//打印所有作家的名字
List<Author> authors = getAuthors();
authors.stream()
.map(author->author.getName())
.forEach(name->System.out.println(name));
中间操作之distinct
可以去除流中的重复元素
流中的distinct()是依赖Object中的equals方法来判断是否为相同对象的。所以使用distinct方法需要重写equals方法;
// 打印所有作家的姓名,并且要求其中不能有重复元素。
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName())
.distinct()
.forEach(name -> System.out.println(name));
中间操作之sorted
可以对流中的元素进行排序
流中的sorted()是需要对象去实现是Comparable接口,不然会抛出ClassCastException异常。实现有两种方式,一种是在操作的对象中实现,第二种是通过匿名内部类的方式去实现。
// 对流中的元素按照年龄进行降厅对非序,并且要求不能有重复的元素
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted()
.forEach(author->System.out.println(author.getAge()));
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1,o2)->o2.getAge()-o1.getAge())
.forEach(author->System.out.println(author.getAge()));
中间操作之limit
limit(n)用法(截取n个元素),超出部分被抛出
// 对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家的姓名
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1,o2)->o2.getAge()-o1.getAge())
.limit(2)
.forEach(author->System.out.println(author.getName()));
中间操作之skip
skip(n)用法(跳过第n个元素)
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.skip(1)
.forEach(author -> System.out.println(author.getName()));
中间操作之flatMap
map只能把一个对象转换成另一个对象来作为流中的元素,而flatMap可以把一个对象转换成多个对象作为流中的元素。
// 打印所有书籍的名字。要求对重复的元素进行去重。
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author->author.getBooks().stream())
.distinct()
.forEach(book->System.out.println(book.getName()));
// 打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式:哲学,爰情
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book->Stream.of(book.getCategory().split(",")))
.distinct()
.forEach(str->System.out.println(str));
3.终结操作
流必须要要有终结操作,否则无法执行。流被终结以后无法再次使用。
终结操作forEach()
- 对流中的元素进行遍历操作
终结操作count()
- 返回此流中元素的计数
//打印这些作家的所出书籍的数目,注意删除重复元素。
List<Author> authors = getAuthors();
long count= authors.stream()
.flatMap(author->author.getBooks().stream())
.distinct()
.count();
System.out.println(count);
终结操作max()&min()
- 获取流中的最值
// 分别获取这些作家的所出书籍的最高分和最低分并打印。
List<Author> authors = getAuthors();
Optional<Book> optional = authors.stream().flatMap(author -> author.getBooks().stream()).distinct()
.max((b1, b2) -> b1.getScore() - b2.getScore());
optional.ifPresent(book -> System.out.println(book.getScore()));
List<Author> authors = getAuthors();
Optional<Book> optional = authors.stream().flatMap(author -> author.getBooks().stream()).distinct()
.min((b1, b2) -> b1.getScore() - b2.getScore());
optional.ifPresent(book -> System.out.println(book.getScore()));
终结操作collect()
- 是将流中的元素转为一个集合
// 获取一个存放所有作者名字的List集合
List<Author> authors = getAuthors();
List<String> list = authors.stream().map(author -> author.getName()).distinct().collect(Collectors.toList());
for (String s : list) {
System.out.println(s);
}
// 获取一个所有书名的Set集合
List<Author> authors1 = getAuthors();
Set<String> set = authors1.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book->book.getName())
.collect(Collectors.toSet());
for (String s : set) {
System.out.println(s);
}
// 获取一个Map集合,map的key为作者名,value为List<Book>
List<Author> authors2 = getAuthors();
Map<String,List<Book>> map = authors2.stream()
.distinct()
.collect(Collectors.toMap(author->author.getName(), value->value.getBooks()));
for (Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) {
String string = iterator.next();
System.out.println(string);
}
终结操作anyMatch()
- 根据逻辑判断返回布尔值
List<Author> authors = getAuthors();
boolean anyMatch = authors.stream()
.map(author -> author.getName())
.distinct()
.anyMatch(name -> name.length() > 2);
终结操作allMatch()
- 可以用来判断是否都符合逻辑判断条件,返回布尔值
List<Author> authors = getAuthors();
boolean allMatch = authors.stream()
.distinct()
.allMatch(author -> author.getAge() > 60);
System.out.println("allMatch: " + allMatch);
终结操作noneMatch()
- 可以用来判断是否都卢符合逻辑判断条件,返回布尔值
List<Author> authors = getAuthors();
boolean noneMatch = authors.stream()
.distinct()
.noneMatch(author -> author.getAge() > 60);
System.out.println("noneMatch: " + noneMatch);
终结操作findAny()
- 查找流中的任意一元素该方法无法保证获取的元素一定是流中的第一个元素
List<Author> authors = getAuthors();
Optional<Author> optional1 = authors.stream()
.distinct()
.filter(author->author.getAge()>30)
.findAny();
optional1.ifPresent(author->System.out.println(author.getName()));
终结操作findFirst()
- 获取流中的第一个元素
List<Author> authors = getAuthors();
Optional<Author> optional2 = authors.stream()
.distinct()
.filter(author->author.getAge()>30)
.findFirst();
optional2.ifPresent(author->System.out.println(author.getName()));
终结操作reduce()
对流中的数据按照你制定的计算方式计算出一个结果
reduce的作用是把stream中的元素给组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和在初始化值的基础上进行计算,计算结果再和后面的元素计算
它内部的计算方式如下:
T result = identity;
for(T element : this stream){
result = accumlator.apply(result,element);
}
//计算年龄的和
List<Author> authors = getAuthors();
int sum = authors
.stream()
.distinct()
.mapToInt(author -> author.getAge())
.reduce(0,(result, element) -> result + element);
System.out.println(sum);
//计算年龄最大值
int value= authors
.stream()
.distinct()
.mapToInt(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (r,e)->r<e?e:r);
System.out.println(value);
//计算年龄最小值
int value1= authors
.stream()
.distinct()
.mapToInt(author -> author.getAge())
.reduce(Integer.MAX_VALUE, (r,e)->r>e?e:r);
System.out.println(value1);
Optional
养成使用Optional的习惯可以写出更加优雅的代码来避免空指针异常。
实际开发过程中很多数据是通过数据库来获取的,Mybatis从3.5版本以后也支持Optional了。可以直接把dao方法的返回值类型定义为Optional类型,Mybatis会自己把数据封装为Optional对象返回。封装的过程也不需要我们自己去操作。
很多函数式编程相关的Api都用了Optional。
Optional.ofNullable() 将对象封装为Optional对象。无论传入的参数是否为null都不会出现问题。(建议使用 )
Optional.of() 传入的参数必须不能为null。(不建议使用)
Optional.empty() 返回一个空的Optional对象。
Optional.ifPresent() 该方法会判断其内部封装的数据是否为空,不为空的时候才能执行具体的消费代码。
Optional.isPresent() 该方法会判断其内部封装的数据是否为空,为空返回false,不为空返回true.
Optional.filter() 在方法中进行逻辑判断,如果满足会返回Optional对象;不满足则返回null.
Optional.map() 将对象中的值转为Optional<List<T>>对象.
Author author = getAuthor();
Optional<Author> option = Optional.ofNullable(author);
System.out.println(Optional.empty().isEmpty());
System.out.println(option.isEmpty());
System.out.println(option.isPresent());
// 存在就打印
option.ifPresent(athor -> System.out.println(author.getIntro()));
// 过滤
option.filter(author1 -> author1.getAge() > 30).ifPresent(ahtor -> System.out.println(author.getName()));
// 转换
option.map(author2 -> author2.getName()).ifPresent(name -> System.out.println(name));
如果想要安全的获取Optional对象中的值,不推荐使用get()方法。推荐使用以下几种方法。
Optional.orElseGet() 如果Optional中的值为null,可以自定义返回一个对象。
Optional.orElseThrow() 如果Optional中的值为null,可以手动抛出异常。
函数式接口
JDK的函数式接口中都加上了
@FunctionalInterface注解进行标识,无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。
JDK自带的常用函数式接口
-
Comparator 消费接口
-
Function 计算转换接口
-
Predicate 判断接口
-
Supplier 生产型接口
方法引用
方法引用的基本格式
类名或对象名::方法名
使用前提
1、如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法
2、如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法
3、如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法
并行流
当流中有大量元素,可以使用并行流提高操作效率。其实并行流就是把任务分配给多个线程去完成。如果使用Stream的话,只需要修改一个方法的调用就可以使用并行流来提高效率
stream().parallel()方法
parallelStream() 直接获取并行流