1.Lambda表达式
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
1.1语法
Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符 或箭头操作符。它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的参数列表 (其实就是接口中的抽象方法的形参列表)
**右侧:**指定了 Lambda 体,是抽象方法的实现逻辑,(其实就是重写的抽象方法的方法体)
2.函数式接口
什么是函数式(Functional)接口
只包含一个抽象方法的接口,称为函数式接口。
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口
2.1理解函数式接口
Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即**java不但可以支持OOP还可以支持OOF(面向函数编程) **
在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
简单的说,在Java8中,**Lambda表达式就是一个函数式接口的实例。**这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!((针对于情况1和情况2))
格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
如下三种主要使用情况:
对象::实例方法名
类::静态方法名
类::实例方法名
函数式接口:
3.创建流
单列集合:
集合对象.stream( )
List<Integer> list = new ArrayList();
Stream<Integer> stream = list.stream();
双列集合:
转化为单列集合再进行创建
Map<String,Integer> map = new HashMap();
map.put("蜡笔小新",19);
Stream<Map.Entry<String,Integer>> stream = map.entrySet().stream();
数组:
Array.stream(数组)
int[] arr = {1,2,3,4,5};
Stream<Integer> arrStream = Arrays.stream(arr);
4.中间操作
filter
// 对作家名字的长度进行筛选
List<Author> author = new ArrayList();
authors.stream
.filter(author->author.getName().length()>1)
//中间操作之后一定要加入一个终结操作
.forEach(author->sout(author.getName()));
map
把流当中的元素进行转换或计算、
@Test
public void test04(){
List<Author> authors = getAuthors();
authors.stream()
.map(new Function<Author, String>() {
@Override
public String apply(Author author) {
return author.getAge().toString();
}
})
.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s)+10;
}
})
.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer s) {
System.out.println(s);
}
});
}
sorted
sorted 在没有传参的情况下 需要对象去实现comparable 接口
/**
* 使用sorted时比较整个元素时,要实现比较接口,并重写方法
*/
@Override
public int compareTo(Author o) {
// return 0; // 这里是如何比较
// 0表示年纪一样大,负数表示传入的大
// 这里sorted如果输出的是降序,你就把这俩顺序对换就可以了,不用记忆
return o.getAge() - this.getAge();
}
/**
* sorted()有两种 一种是不含参数的,另外一种是含有参数的
*
*/
@Test
public void test06(){
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted()
.forEach(author -> System.out.println(author.getName()));
}
@Test
public void test07(){
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return 0;
}
})
.forEach(author -> System.out.println(author));
}
limit限制流的长度
@Test
public void test08(){
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1,o2)->o1.getAge()-o2.getAge())
// 限制流的长度 还是比较好用的
.limit(3)
.forEach(author -> System.out.println(author.getName()));
}
skip(n)其中的n指的是跳过前n个元素
flatMap可以把一个对象转化成多个对象
@Test
public void test09(){
List<Author> authors = getAuthors();
authors.stream()
// 将List<Book> 转化为多个Book对象
.flatMap(author -> author.getBookList().stream())
.distinct()
.forEach(book -> System.out.println(book.getName()));
}
就是直接把一个对象转化为多个对象,这里可能有点不好理解
5.终结操作
注意一定要有终结的操作要不然不会出现效果的
forEach对流当中的元素进行遍历操作
count返回流当中元素的个数
// count()返回的是long类型的
@Test
public void test11(){
List<Author> authors = getAuthors();
System.out.println(authors.stream()
.distinct()
.count());;
}
max 和 min 选取最大数值和最小数值
@Test
public void test12(){
List<Author> authors = getAuthors();
System.out.println(authors.stream()
.map(author -> author.getAge())
.max(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
}).get());
}
collect将流转化为集合 List Map 和 Set
List 是有序集合,Set 和 Map 都是无序集合,List 集合的元素可以重复,Set 和 Map 的键不能重复,但是 Map 的值可以重复。值得注意的是,如果在 Map 中添加重复的键,那么该键对应的值会覆盖掉原来的值。
/**
* 将数据转化为set
* @return
*/
/**
* 将数据转化为list
* @return
*/
@Test
public void test13(){
List<Author> authors = getAuthors();
// stream流 转化为 List 集合
List<Author> collect = authors.stream()
.collect(Collectors.toList());
// stream流 转化为 Set集合
Set<Author> collect1 = authors.stream()
.collect(Collectors.toSet());
}
将流转化为map
/**
* 将流转化为map
* @return
*/
public void test14(){
List<Author> authors = getAuthors();
Map<String, List<Book>> collect = authors.stream()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBookList()));
}
查找和匹配
anyMatch查找是否含有匹配的元素,如果含有匹配的元素返回true 没有匹配的元素返回false
@Test
public void test15(){
List<Author> authors = getAuthors();
boolean b = authors.stream()
// 任意返回条件 任意的元素符合条件就会返回true
// 没有符合条件的就会返回false
.anyMatch(author -> author.getAge() < 13);
System.out.println(b);
}
allMatch所有的元素都匹配的话,才会返回true else 返回 false
@Test
public void test16(){
List<Author> authors = getAuthors();
System.out.println(authors.stream()
.allMatch(author->author.getAge()<13));
}
查找:
findAny 获取流当中的元素 该方法没有办法保证一定是流当中的第一个元素
findFirst 获取流当中的第一个元素
reduce归并缩减操作 对流当中的数据按照你指定的方式计算出来一个结果
reduce+map来进行简化映射
@Test
public void test17(){
List<Author> authors = getAuthors();
System.out.println(authors.stream()
.map(author -> author.getAge())
// 第一个元素的值是 传入的0
.reduce(0, (sum, elment) -> sum + elment));
}
reduce+map来求取年龄的最大值
@Test
public void test18(){
List<Author> authors = getAuthors();
System.out.println(authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (min, element) -> element > min ? element : min));
}
流的特点:
惰性求值(如果没有终结操作,只有中间操作是不会去执行的)
流是一次性的
不会影响原始数据
6.optional
解决空指针异常,之前都是做个非空的判断
创建optional对象
Optional.ofNullable()方法 可以搞去空指针异常
@Test
public void test19(){
List<Author> authors = getAuthors();
for (Author author :authors){
//创建optional对象
Optional<Author> author1 = Optional.ofNullable(author);
// 通过consumer来摆除空指针异常
author1.ifPresent(author2 -> System.out.println(author2.getName()));
}
}
optional.of()方法不推荐使用
optional.get()碰到空的值还是会抛出空指针异常
所以推荐使用orElseGet()当碰到null的时候,会指出默认的数值
@Test
public void test21(){
// orElseGet方法的使用
Author newAut = null;
Author author1 = new Author(1L, "杨杰炜", "my introduction 1", 18, null);
System.out.println(Optional.ofNullable(newAut)
.orElseGet(() -> new Author(2L, "wsy", "my introduction 2", 19, null)).getName());
}
使用filter可以进行数据的过滤:
isPresent可以用来判断是否存在optional对象
ifPresent(new Consumer(){}如果存在的话则
optional同样存在map的方法可以进行数据的转换
常用的就是ofNullable+ifPresent()方法来进行
7.函数式接口
是否含有一个方法来判断是否是函数式接口
一般函数式接口会有@FunctionalInterface的注解
常见的函数式接口有:
consumer消费接口 比如在其中进行打印
Public interface consumer{
void accept();
}
function计算转换接口
Public interface Function{
R apply(T t);
}
predicate判断型接口 进行判断的
public interface Predicate<T>{
boolean test(T t);
}
supplier生产型接口 生产出需要的对象 把想要的对象生产之后拿过来
public imterface Supplier<T>{
T get();
}
and方法的使用
方法引用
如果方法体当中只有一个方法的调用的时候,可以使用方法引用
格式
类名这个强调的是全部的参数
类名::方法名
对象名::方法名
mapToInt int类型相比包装类的Integer来说有更大的效率
基本数据类型的优化
并行流:
peek是用来断点调试的
parallel可以把串行流转化为并行流