1.Lambda表达式
1.1概述
Lambda表达式是JDK8中的一个语法糖,他可以对某些匿名内部类的写法进行转化。它是函数式编程思想的一个重要体现。让我们不用关注什么是对象,而更关注我们对数据进行了什么操作。
1.2核心原则
可推导可省略
1.3基本格式
(参数列表)->{代码},参数列表是接口
我们在创建线程并启动的时候可以使用匿名内部类,写法如下:
例1:
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("线程开始执行");
}
}).start();
我们使用Lambda表达式进行简化
new Thread(()->{
System.out.println("线程开始执行");
}).start();
```
例2:
```bash
public static void main(String[] args) {
//1.匿名内部类的写法
printNum(new IntPredicate() {
@Override
public boolean test(int value) {
return value%2==0;
}
});
//2.Lambda表达式的写法
printNum((int value)->{
return value%2==0;
});
}
public static void printNum(IntPredicate predicate){
int[] arr = {1,2,3,4,5,6,7,8};
for (int i :arr){
if(predicate.test(i)){
System.out.println(i);
}
}
}
例3
public static void main(String[] args) {
//1.匿名内部类
Integer res1 = typeConvert(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
//2.lambda表达式
Integer res2 = typeConvert((String s) -> {
return Integer.valueOf(s);
});
System.out.println(res2);
}
public static <R> R typeConvert(Function<String,R> function){
String str="123456";
R result = function.apply(str);
return result;
}
例4
public static void main(String[] args) {
//1.匿名内部类的写法
foreachArr(new IntConsumer() {
@Override
public void accept(int value) {
System.out.println(value);
}
});
//2.lambda表达式的写法
foreachArr((int value)->{
System.out.println(value);
});
}
public static void foreachArr(IntConsumer consumer){
int[] arr = {1,2,3,4,5,6,7,8,9};
for (int i :arr){
consumer.accept(i);
}
}
1.4省略规则,直接alt+enter
(1)参数类型可以省略;
(2)方法体只有一句代码的时候,大括号、return和唯一一句代码的分号可以省略;
(3)方法只有一个参数时小括号可以省略
(4)如果记不住,直接alt+enter
例1
public static void main(String[] args) {
//1.匿名内部类的写法
foreachArr(new IntConsumer() {
@Override
public void accept(int value) {
System.out.println(value);
}
});
//2.lambda表达式的写法 最精简版
foreachArr(value->
System.out.println(value)
);
}
public static void foreachArr(IntConsumer consumer){
int[] arr = {1,2,3,4,5,6,7,8,9};
for (int i :arr){
consumer.accept(i);
}
}
例2
public static void main(String[] args) {
//1.匿名内部类
Integer res1 = typeConvert(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
//2.lambda表达式
Integer res2 = typeConvert((String s) -> {
return Integer.valueOf(s);
});
//3.最省略的lambda表达式,能记住直接手写,记不住直接alt+enter
typeConvert(s->Integer.valueOf(s));
System.out.println(res2);
}
public static <R> R typeConvert(Function<String,R> function){
String str="123456";
R result = function.apply(str);
return result;
}
例3
public class Demoo5 {
public static void main(String[] args) {
//1.匿名内部类
int calculateRes = calculate(new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
});
System.out.println(calculateRes);
//2.Lambda表达式
int calculateRes2 = calculate((left, right) -> left + right);
System.out.println(calculateRes2);
}
public static int calculate(IntBinaryOperator operator){
int a = 10 ;
int b = 20 ;
return operator.applyAsInt(a, b);
}
}
2.Stream表达式
例1
public class StringDemo1 {
public static List<Author> getAuthors(){
//数据初始化
Author author1 = new Author(1L,"蒙多",12,"牛逼666",null);
Author author2 = new Author(2L,"剑豪",35,"hasake",null);
Author author3 = new Author(3L,"剑圣",36,"剑圣牛逼666",null);
//书籍列表
List<Book> books1 = new ArrayList<>();
List<Book> books2 = new ArrayList<>();
List<Book> books3 = new ArrayList<>();
books1.add(new Book(1L,"风雨剑","个人专辑",100,"哈萨恪"));
books1.add(new Book(2L,"剑圣之剑","个人专辑",100,"我的剑就是你的剑"));
books1.add(new Book(3L,"诺手","个人专辑",100,"诺手牛逼"));
author1.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
List<Author> arrayList = new ArrayList(Arrays.asList(author1, author2, author3));
return arrayList;
}
public static void main(String[] args) {
getMethod1();
}
//1.需求:调用getAuthors()方法获取作家的集合。现在需要打印出所有年龄小于18岁的作家的名字,并且要注意去重
public static void getMethod1(){
List<Author> authors = getAuthors();
authors.stream()//把集合转换成流
.distinct() //去重
.filter(author->author.getAge()<18) //筛选出年龄小于18岁的
.forEach(author->{
System.out.println(author.getName());
});//遍历打印
}
}
2.1Stream流的常用操作
2.1.1创建流
(1)单列集合List:集合对象.stream()
List<Author> authors = getAuthors();
authors.stream()//把集合转换成流
案例:
//2.常用操作List:直接数组.stream()
public static void getMethod2() {
Integer[] arr = {1,2,3,4,5};
//方式一:使用Arrays.stream(数组)
Stream<Integer> stream = Arrays.stream(arr);
//方式二:使用Stream.of(数组)
Stream<Integer> arr1 = Stream.of(arr);
//方式三:双列集合map,先转换成单列集合再创建
Map<String,Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
Stream<Map.Entry<String, Integer>> stream1 = map.entrySet().stream();
}
(2)数组:Arrays.stream(数组),或者使用Stream.of()
Integer[] arr = {1,2,3,4,5};
//方式一:使用Arrays.stream(数组)
Stream<Integer> stream = Arrays.stream(arr);
//方式二:使用Stream.of(数组)
Stream<Integer> arr1 = Stream.of(arr);
案例:
//3.数组转换成流,打印小于3的数组
public static void getMethod3() {
Integer[] arr = {1,2,3,4,5};
Arrays.stream(arr).distinct()//去重
.filter(i->i<3) //取出i<3的值
.forEach(
System.out::println
);//打印
}
(3)双列集合Map:转换成单列集合再创建
//方式三:双列集合map,先转换成单列集合再创建
Map<String,Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
Stream<Map.Entry<String, Integer>> stream1 = map.entrySet().stream();
案例:找出年龄等于24的人
//3.Map转换成流
public static void getMethod4() {
Map<String,Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.entrySet().stream().distinct() //先去重
.filter(entry->entry.getValue()>24)//过滤
.forEach(entry->{
System.out.println("姓名 = "+entry.getKey()+"年龄 = "+entry.getValue());
});
}
2.1.2中间操作filter,可以对于流中的元素进行条件过滤,符合过滤条件的才能继续留在流中
案例1:打印出所有姓名长度大于3的作家姓名
//5.打印出所有姓名长度大于3的作家姓名
public static void getMethod5() {
List<Author> authors = getAuthors();
authors.stream().
filter(author->author.getName().length()>3)
.forEach(author -> System.out.println(author.getName())
);
}
2.1.3中间操作map,可以对流中的元素进行计算或者转换
案例2:打印出所有作家的姓名,author经过map转换之后的流是name
//6.打印出所有作家的姓名
public static void getMethod6() {
List<Author> authors = getAuthors();
authors.stream().
map(author->author.getName())
.forEach(name -> System.out.println(name));
}
案例3:打印出所有作家年龄加10
//7.打印出所有作家的年龄+10
public static void getMethod7() {
List<Author> authors = getAuthors();
authors.stream().
map(author->author.getAge()).
map(age->age+10).
forEach(age -> System.out.println(age));
}
2.1.4中间操作distinct:可以去除流中重复的元素。要注意distinct方法是依赖Object的equals方法来判断是否是相同对象的,所以需要注意重写equals方法
案例4:打印出所有作家的姓名,并且要求不能有重复元素
//8.打印出所有作家的姓名,并且要求不能有重复元素
public static void getMethod8() {
List<Author> authors = getAuthors();
authors.stream().
distinct().
forEach(author -> System.out.println(author.getName()));
}```
![在这里插入图片描述](https://img-blog.csdnimg.cn/b0cd952c4fc84c1a9510588e44c750be.png)
### 2.1.5中间操作sorted:可以对流中的元素进行排序。如果调用空参的sorted方法,流中的元素需要实现了Comparable。
案例5:对作家元素按照年龄进行降序排序,并且要求不能有重复的元素。
```bash
//9.对作家元素按照年龄进行降序排序,并且要求不能有重复的元素
public static void getMethod9() {
List<Author> authors = getAuthors();
//如果不知道lambda表达式怎么写,就需要先写匿名内部类的形式
//匿名内部类
authors.stream().
distinct().
sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return o2.getAge()-o1.getAge();
}
}).
forEach(author -> System.out.println(author));
//lambda表达式
authors.stream().
distinct().
sorted((o1, o2) -> o2.getAge()-o1.getAge()).
forEach(author -> System.out.println(author));
}
2.1.6中间操作limit:可以设置流的最大长度,超出的部分将被抛弃
案例6:对流中的元素按照年龄进行降序排列,并且要求不能有重复元素,然后打印其中年龄最大的两个作家姓名
//11.对流中的元素按照年龄进行降序排列,并且要求不能有重复元素,然后打印其中年龄最大的两个作家姓名
public static void getMethod11() {
// 对流中的元素按照年龄进行降序排列,并且要求不能有重复元素,然后打印其中年龄最大的两个作家姓名
List<Author> authors = getAuthors();
authors.stream().distinct().
sorted((o1,o2)->o2.getAge()-o2.getAge()).
limit(2).
forEach(author -> System.out.println(author));
}
2.1.7中间操作skip:跳过流中前n个元素,返回剩下的元素
案例7:打印出年龄最大的作家之外的其他作家,要求不能有重复元素,并且按照年龄降序排序
//11.打印出年龄最大的作家之外的其他作家,要求不能有重复元素,并且按照年龄降序排序
public static void getMethod12() {
List<Author> authors = getAuthors();
authors.stream().
distinct().
sorted((o1,o2)->o2.getAge()-o1.getAge()).
skip(1).
forEach(author -> System.out.println(author));
}
2.1.8中间操作flatmap:map只能把一个对象转换成另一个对象作为流中的元素,而flatMap可以把一个对象转换为多个对象作为流中的元素
案例8:打印出所有书籍的名字,要求对重复元素进行去重
//13.打印出所有书籍的名字,要求对重复元素进行去重
public static void getMethod13() {
List<Author> authors = getAuthors();
authors.stream().
flatMap(author->author.getBooks().stream()).
distinct().
forEach(book -> System.out.println(book));
}
案例9:打印出现有数据的所有分类,要求对数据进行分类去重,不能出现这种格式:哲学,爱情
//14.打印出现有数据的所有分类,要求对数据进行分类去重,不能出现这种格式:哲学,爱情
public static void getMethod14() {
List<Author> authors = getAuthors();
authors.stream().
flatMap(author->author.getBooks().stream()).
distinct().
flatMap(book -> Arrays.stream(book.getCategory().split(","))).
distinct().
forEach(category-> System.out.println(category));
}
2.2 终结操作
2.2.1foreach:遍历
2.2.2count:可以用来获取当前流中元素的个数
案例1:打印这些作家所处书籍的树木,注意删除重复元素
public static void test1(){
long booksNum = getAuthors().stream()
.distinct()
.flatMap(author -> author.getBooks().stream())
.distinct()
.count();
System.out.println("书籍数量 = "+booksNum);
}
2.2.3 max&min
案例3:分别获取这些作家所处书籍的最高分和最低分并打印
public static void test3(){
Optional<Integer> max = getAuthors().stream()
.distinct()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((o1, o2) -> o1 - o2);
Optional<Integer> min = getAuthors().stream()
.distinct()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((o1, o2) -> o2 - o1);
System.out.println("最高分 = "+max.get());
System.out.println("最低分 = "+min.get());
}
2.2.4终结操作collect:把当前流转换为一个集合
(1)案例4:获取存放所有作者名字的List集合
public static void test4() {
List<String> authors = getAuthors().stream()
.map(author -> author.getName())
.collect(Collectors.toList());
System.out.println(authors);
}
(2)案例5:获取所有书名的set集合
public static void test5(){
Set<String> authors = getAuthors().stream()
.map(author -> author.getName())
.collect(Collectors.toSet());
System.out.println(authors);
}
(3)获取一个map集合,map的key为作者名字,value为List
public static void test6(){
Map<String, List<Book>> collect = getAuthors().stream()
.distinct()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
System.out.println(collect);
}
2.2.5查找与匹配anyMatch:可以用来判断是否有任意匹配条件的元素,结果为double类型
案例6:判断是否有年龄在29岁以上的作家
public static void test7(){
boolean b = getAuthors().stream().anyMatch(author -> author.getAge() > 36);
System.out.println(b);
}
2.2.6查找与匹配allMatch:可以用来判断是否都符合匹配条件,结果为double类型。如果都符合结果为true,否则为false
案例7:判断是否所有作家都是成年人
public static void test8(){
boolean b = getAuthors().stream().allMatch(author -> author.getAge() > 18);
System.out.println(b);
}
2.2.7查找与匹配noneMatch:可以判断流中元素是否都不符合匹配条件,如果都不符合结果为true,否则结果为false
案例8:判断做节是否都没有超过100岁的
public static void test9(){
boolean b = getAuthors().stream().noneMatch(author -> author.getAge() > 100);
System.out.println(b);
}
2.2.8 查找与匹配findAny:获取流中任意一个元素。
案例9:获取任意一个大于18岁的作家,如果存在就输出他的名字
public static void test10(){
Optional<Author> optionalAuthor = getAuthors().stream()
.filter(author -> author.getAge() > 18)
.findAny();
optionalAuthor.ifPresent(author -> System.out.println(author));
}
2.2.9查找与匹配findFirst:获取流种的第一个元素
案例10:获取年龄最小的作家,并输出他的名字
public static void test11(){
Optional<Author> first = getAuthors().stream()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.findFirst();
first.ifPresent(author -> System.out.println(author));
}
2.2.10 终结操作之reduce归并操作:对流中的数据按照你指定的计算方式得出一个结果。reduce的作用是把stream中的元素给组合起来,我们可以传一个初始值,它会按照我们的计算方式依次拿到流中的元素和在初始化值进行计算,计算结果再和后面的元素计算
内部的计算方式如下:
T result = identity;
for(T element :this stream){
result = accumulator.apply(result,element)
}
return result;
案例12:使用reduce求所有作者年龄的和
public static void test12() {
Integer sum = getAuthors().stream()
.map(author -> author.getAge())
.reduce(0, (result, element) -> result + element);
System.out.println(sum);
}```
案例13:使用reduce求所有作者中年龄的最大值
```bash
public static void test13() {
Integer res = getAuthors()
.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
System.out.println(res);
}```
案例14:使用reduce求所有作者中年龄最小的
```bash
public static void test14() {
Integer min = getAuthors()
.stream()
.map(author -> author.getAge())
.reduce(Integer.MAX_VALUE, (result, element) -> result < element ? result : element);
System.out.println(min);
}
2.2.11Stream流使用注意事项
(1)惰性求值(如果没有终结操作,没有终结操作是不会得到执行的)
(2)流是一次性的(一旦一个流对象经过终结操作,这个流就不能再被使用了)
(3)不影响原数据(流中可以对很多数据进行处理,但是正常情况下是不会影响原来集合中的元素的,这也是我们期望的)
3.Optional
3.1概述
我们在编写代码的时候出现最多的就是空指针异常。所以在很多时候我们需要各种判断,例如:
Author author = getAuthor();
if(author!=null){
System.out.println(author.getName());
}
尤其是对象中的属性还是一个对象的情况下,这种判断会更多。而过多的判断语句会让我们的代码明显变得臃肿不堪。所以在JDK8中引入了Optional来避免空指针。并且很多函数式编程相关的API中也都用到了Optional。
3.1使用
3.1.1创建对象
Optional就好像是包装类,可以包我们的具体数据封装Optional对象内部,然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常。
List<Author> authors = getAuthors();
Optional<List<Author>> authorsOptional = Optional.ofNullable(authors);
你可能觉得还要加一行代码来封装数据比较麻烦,但是如果改造下getAuthor方法,让其返回值就是封装号的Optional的话,我们在使用的时候就会方便很多。而且在实际开发过程中,我们很多的数据都是从数据库中获取的,Mybatis3.5版本已经支持Optional了,我们可以直接把dao方法的返回值定义成Optional的类型,Mybatis自己就会把数据封装成Optional对象返回,封住囊的过程不需要我们自己操作。如果你确定一个对象不是空的,则可以使用Optional的静态方法of来把数据封装成Optional对象。但是要注意,如果使用of的时候,传入的参数必须不能为Null。
List<Author> authors = getAuthors();
Optional<List<Author>> authors1 = Optional.of(authors);
如果一个方法的返回值类型是Optional类型,而如果我们经过判断发现某次计算得到的返回值是null,这时候就需要把null封装成Optional对象返回,这时候可以使用Optional的静态方法empty来进行封装。