Lambda表达式(三更草堂)

一、函数式编程-Stream流

1、概述

1.1 为什么学?

在这里插入图片描述

1.2 函数式编程思想

1.2.1 概念

在这里插入图片描述

1.2.2 优点

在这里插入图片描述

2、Lambda表达式

2.1 概述

在这里插入图片描述

2.2 核心原则

可推导可省略

2.3 基本格式

(参数列表)->{代码}

2.3.1 例一

    public static void main(String[] args) {
        //优化前
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程中run方法被执行了!");
            }
        }).start();

        //优化后 (参数列表)->{代码}
        new Thread(() -> {
            System.out.println("新线程中run方法被执行了!");
        }).start();
        
        //进一步优化
        new Thread(() -> System.out.println("新线程中run方法被执行了!")).start();
    }

2.3.2 例二

    public static void main(String[] args) {
        //优化前
        int result1 = calculateNum(new IntBinaryOperator() {
            @Override
            public int applyAsInt(int left, int right) {
                return left + right;
            }
        });
        System.out.println(result1);

        //优化后
        int result2 = calculateNum((int left, int right) -> {
            return left + right;
        });
        System.out.println(result2);
        
        //进一步优化
        int result3 = calculateNum((left, right) -> left + right);
        System.out.println(result3);
    }

    public static int calculateNum(IntBinaryOperator operator) {
        int a = 10;
        int b = 20;
        return operator.applyAsInt(a, b);
    }


2.3.3 例三

 public static void main(String[] args) {
        //优化前
        printNum(new IntPredicate() {
            @Override
            public boolean test(int value) {
                return value % 2 == 0;
            }
        });

        //优化后
        printNum((int value) -> {
                    return value % 2 == 0;
                }
        );
        
        //进一步优化
        printNum(value -> value % 2 == 0);

    }
    
    public static void printNum(IntPredicate predicate) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        for (int i : arr) {
            if (predicate.test(i)) {
                System.out.println(i);
            }
        }
    }

2.3.4 例四

    public static void main(String[] args) {
        //优化前
        Integer result1 = typeConver(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.valueOf(s);
            }
        });
        System.out.println(result1);//12345

        //优化后
        Integer result2 = typeConver((String s) -> {
            return Integer.valueOf(s);
        });
        System.out.println(result2);//12345
        
        //进一步优化
        Integer result3 = typeConver(s -> Integer.valueOf(s));
        System.out.println(result3);//12345
       
    }

    public static <R> R typeConver(Function<String, R> function) {
        String str = "12345";
        R result = function.apply(str);
        return result;
    }

2.3.5 例五

		//优化前
        foreachArr(new IntConsumer() {
            @Override
            public void accept(int value) {
                System.out.println(value);
            }
        });

        //优化后
        foreachArr((int value) -> {
            System.out.println(value);
        });
        
        //进一步优化
        foreachArr(value -> System.out.println(value));
    }

    public static void foreachArr(IntConsumer consumer) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        for (int i : arr) {
            consumer.accept(i);
        }
    }

2.4 省略规则

在这里插入图片描述

3、Stream流

3.1 概述

在这里插入图片描述

3.2 案例数据准备

		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //作品
    private List<Book> books;

}
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Book {
    //id
    private Long id;
    //书名
    private String name;
    //分类
    private String category;//"哲学,小说"
    //评分
    private Integer score;
    //简介
    private String intro;

}
 private static List<Author> getAuthors() {
        //数据初始化
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        Author author2 = new Author(2L, "亚拉索", 15, "狂风也追逐不上他的思考速度", null);
        Author author3 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
        Author author4 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);

        //书籍列表
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学,爱情", 88, "用一把刀划分了爱恨"));
        books1.add(new Book(2L, "一个人不能死在同一把刀下", "个人成长,爱情", 99, "讲述如何从失败中明悟真理"));

        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(4L, "吹或不吹", "爱情,个人传记", 56, "一个哲学家的恋爱观注定很难把他所在的时代理解"));

        books3.add(new Book(5L, "你的剑就是我的剑", "爱情", 56, "无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList<>(Arrays.asList(author, author2, author3, author4));
        return authorList;
    }

3.3 快速入门

3.3.1 需求

在这里插入图片描述

3.3.2 实现

//优化前
private static void test01() {
        List<Author> authors = getAuthors();
        Stream<Author> stream = authors.stream();
        //把集合转换成流
        stream
                //去重
                .distinct()
                //过滤条件
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge() < 18;
                    }
                })
                //打印
                .forEach(new Consumer<Author>() {
                    @Override
                    public void accept(Author author) {
                        System.out.println(author.getName() + "今年" + author.getAge() + "岁");
                    }
                });
    }
    
//优化后
private static void test01() {
        List<Author> authors = getAuthors();
        //把集合转换成流
        authors.stream()
                //去重
                .distinct()
                //过滤条件
                .filter(author -> author.getAge() < 18)
                //打印
                .forEach(author -> System.out.println(author.getName() + "今年" + author.getAge() + "岁"));
    }

3.4 常用操作

3.4.1 创建流

① 单列集合

集合对象.stream( )

private static void test01() {
        List<Author> authors = getAuthors();
        Stream<Author> stream = authors.stream();
        //把集合转换成流
        stream
                //去重
                .distinct()
                //过滤条件
                .filter(author -> author.getAge() < 18)
                //打印
                .forEach(author -> System.out.println(author.getName() + "今年" + author.getAge() + "岁"));
    }
② 数组

Arrays.stream(数组)或者使用Stream.of来创建

private static void test02() {
        Integer[] arr = {1, 2, 3, 4, 5};
        Stream<Integer> stream1 = Arrays.stream(arr);
        stream1.distinct()
                .filter(integer -> integer > 2)
                .forEach(integer -> System.out.println(integer));

        Stream<Integer> stream2 = Stream.of(arr);
        stream2.distinct()
                .filter(integer -> integer < 2)
                .forEach(integer -> System.out.println(integer));
    }
③ 双列集合

转换成单列集合后再创建

 private static void test03() {
        Map<String, Integer> map = new HashMap<>();
        map.put("蜡笔小新", 5);
        map.put("zyy", 22);
        map.put("zqh", 23);
        Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
        stream.distinct()
                .filter(entry -> entry.getValue() > 5)
                .forEach(entry -> System.out.println(entry.getKey() + "==" + entry.getValue()));
    }

3.4.2 中间操作

① filter

可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中。

例如:打印所有姓名长度大于1的作家的姓名

 private static void test04() {
        List<Author> authors = getAuthors();
        //打印所有姓名长度大于1的作家的姓名
        authors.stream()
                .filter(author -> author.getName().length() > 1)
                .forEach(author -> System.out.println(author.getName()));
    }
② map

可以把对流中的元素进行计算或转换

例如:打印所有作家的名字

 private static void test05() {
        List<Author> authors = getAuthors();
        //打印所有作家的名字
        authors.stream()
                .map(author -> author.getName())
                .map(name -> name + "!")
                .forEach(name -> System.out.println(name));
    }
③ distinct

可以去除流中的重复元素

例如:打印所有作家的姓名,并且要求其中不能有重复元素

private static void test06() {
        List<Author> authors = getAuthors();
        //打印所有作家的姓名,并且要求其中不能有重复元素
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));
    }

注意:distinct方法是依赖Object的equals方法来判断是否是相同对象的,所以需要注意重写equals方法

④ sorted

可以对流中的元素进行排序

例如:对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素

private static void test07_1() {
        List<Author> authors = getAuthors();
        //对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素
        authors.stream()
                .distinct()
                //降序
                .sorted()
                .forEach(author -> System.out.println(author.getAge()));
    }

注:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable

@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author implements Comparable<Author> {
    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //作品
    private List<Book> books;

    @Override
    public int compareTo(Author o) {
        //比较-本体的是降序
        return o.getAge() - this.getAge();
    }
}
//有参排序
private static void test07_2() {
        List<Author> authors = getAuthors();
        //对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素
        authors.stream()
                .distinct()
                //降序
                .sorted((o1, o2) -> o2.getAge()-o1.getAge())
                .forEach(author -> System.out.println(author.getAge()));
    }
⑤ limit

可以设置流的最大长度,超出的部分将被抛弃

例如:对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家的姓名

 private static void test08() {
        //对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家的姓名
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .limit(2)
                .forEach(author -> System.out.println(author.getName()));

    }
⑥ skip

跳过流中的前n个元素,返回剩下的元素

例如:打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。

 private static void test09() {
        //打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .skip(1)
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .forEach(author -> System.out.println(author.getName()));
    }
⑦ flatMap

map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素

例一:打印所有书籍的名字。要求对重复的元素进行去重。

private static void test10() {
        //打印所有书籍的名字。要求对重复的元素进行去重
        List<Author> authors = getAuthors();
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .forEach(book -> System.out.println(book.getName()));
    }

例二:打印现有数据的所有分类。要求对分类进行去重。不能出现这样格式:哲学,爱情

private static void test11() {
        //打印现有数据的所有分类。要求对分类进行去重。不能出现这样格式:哲学,爱情
        List<Author> authors = getAuthors();
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(name -> System.out.println(name));
    }

3.4.3 终结操作

① forEach

对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作

例子:输出所有作家的名字

 private static void test12() {
        //输出所有作家的名字
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .map(author -> author.getName())
                .forEach(name -> System.out.println(name));
    }
② count

可以用来获取当前流中元素的个数

例子:打印这些作家所出书籍的数目,注意删除重复元素

private static void test13() {
        //打印这些作家所出书籍的数目,注意删除重复元素
        List<Author> authors = getAuthors();
        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);//6
    }
③ min&max

可以用来获取流中的最值

例子:分别获取这些作家的所出书籍的最高分和最低分并打印

private static void test14() {
        //分别获取这些作家的所出书籍的最高分和最低分并打印
        List<Author> authors = getAuthors();
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .distinct()
                .max((score1, score2) -> score1 - score2);
        
        Optional<Integer> min = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .distinct()
                .min((score1, score2) -> score1 - score2);
        System.out.println("最高分:" + max.get());
        System.out.println("最低分:" + min.get());
    }
④ collect

把当前流转换成一个集合

例一:获取一个存放所有作者名字的List集合

private static void test15() {
        //获取一个存放所有作者名字的List集合
        List<Author> authors = getAuthors();
        List<String> nameList = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toList());
        System.out.println(nameList);
    }

例二:获取一个所有书名的Set集合

private static void test16() {
        //获取一个所有书名的Set集合
        List<Author> authors = getAuthors();
        Set<String> bookNameSet = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getName())
                .collect(Collectors.toSet());
        System.out.println(bookNameSet);
    }

例三:获取一个map集合,map的key为作者名,value为List

private static void test17() {
        //获取一个map集合,map的key为作者名,value为List<Book>
        List<Author> authors = getAuthors();
        Map<String, List<Book>> collect = authors.stream()
                .distinct()
                .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
        System.out.println(collect);
    }
⑤ 查找与匹配
1、anyMatch

可以用来判断是否有任意符合匹配条件的元素,结果为boolean类型

例子:判断是否有年龄在29以上的作家

 private static void test19() {
        //判断是否有年龄在29以上的作家
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .map(author -> author.getAge())
                .anyMatch(age -> age > 29);
        System.out.println(flag);
    }
2、allMatch

可以用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否者结果为false

例子:判断是否所有的作家都是成年人

    private static void test20() {
        //判断是否所有的作家都是成年人
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .map(author -> author.getAge())
                .allMatch(age -> age >= 18);
        System.out.println(flag);
    }
3、noneMatch

可以判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false

例子:判断作家是否都没有超过100岁的

private static void test21() {
        //判断作家是否都没有超过100岁的
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .map(author -> author.getAge())
                .noneMatch(age -> age > 100);
        System.out.println(flag);
    }
4、finaAny

获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素

例子:获取任意一个大于18岁的作家,如果存在输出他的名字

 private static void test22() {
        //获取任意一个大于18岁的作家,如果存在输出他的名字
        List<Author> authors = getAuthors();
        Optional<Author> optional = authors.stream()
                .filter(author -> author.getAge() > 18)
                .findAny();
        optional.ifPresent(author -> System.out.println(author.getName()));
    }
5、findFirst

获取流中的第一个元素

例子:获取一个年龄最小的作家,并输出他的姓名

 private static void test23() {
        //获取一个年龄最小的作家,并输出他的姓名
        List<Author> authors = getAuthors();
        Optional<Author> optional = authors.stream()
                .sorted((o1, o2) -> o1.getAge() - o2.getAge())
                .findFirst();
        optional.ifPresent(author -> System.out.println(author.getName()));
    }
⑥ reduce归并

对流中的数据按照你指定的计算方式计算出一个结果。(缩减操作)

reduce的作用是把stream中的元素给组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和初始化值进行计算,计算结果再和后面的元素计算。

reduce两个参数的重载形式内部的计算方式如下:

T result = identity;
for (T element : this stream)
	result = accumulator.apply(result, element)
return result;

其中identity就是我们可以通过方法参数传入的初始值,accumulator的apply具体进行什么计算也是我们通过方法参数来确定的。

例一:使用reduce求所有作者年龄的和

 private static void test24() {
        //使用reduce求所有作者年龄的和
        List<Author> authors = getAuthors();
        Integer sum = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(0, (result, element) -> result + element);
        System.out.println(sum);
    }

例二:使用reduce求所有作者中年龄的最大值

 private static void test25() {
        //使用reduce求所有作者中年龄的最大值
        List<Author> authors = getAuthors();
        Integer max = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
        System.out.println(max);
    }

例三:使用reduce求所有作者中年龄的最小值

 private static void test26() {
        //使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        Integer min = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
        System.out.println(min);
    }

reduce一个参数的重载形式内部的计算

 	 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();

如果用一个参数的重载方法去求最小值代码如下:

 private static void test27() {
        //如果用一个参数的重载方法去求最小值代码如下:
        List<Author> authors = getAuthors();
        Optional<Integer> optional = authors.stream()
                .map(author -> author.getAge())
                .reduce((result, element) -> result > element ? element : result);
        optional.ifPresent(age -> System.out.println(age));
    }

3.5 注意事项

  • 惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
  • 流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
  • 不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的)

4、Optional

4.1 概述

我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断

例如:

  public static void main(String[] args) {
        Author author = getAuthor();
        if(author!=null){
            System.out.println(author.getName());
        }
    }
public static Author getAuthor() {
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟真理的祖安人", null);
        return null;
    }
  • ​尤其是对象中的属性还是一个对象的情况下。这种判断会更多。
  • ​而过多的判断语句会让我们的代码显得臃肿不堪。
  • ​所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。
  • ​并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习造成影响。

4.2 使用

4.2.1 创建对象

Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部。然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常

我们一般使用Optional静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。

 private static void test2() {
        Optional<Author> optional = getAuthorOptional();
        //非空打印
        optional.ifPresent(author1 -> System.out.println(author1.getName()));
    }
public static Optional<Author> getAuthorOptional() {
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟真理的祖安人", null);
        return Optional.ofNullable(author);
    }  

在实际开发中我们的数据很多是从数据库获取的。Mybatis从3.5版本可以也已经支持Optional了。我们可以直接把dao方法的返回值类型定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回。封装的过程也不需要我们自己操作。

如果你确定一个对象不是空的则可以使用Optional静态方法of来把数据封装成Optional对象。

 Author author = new Author();
 Optional<Author> authorOptional = Optional.of(author);

但是一定要注意,如果使用of的时候传入的参数必须不为null。(尝试下传入null会出现什么结果)
在这里插入图片描述

4.2.2 安全消费值

​ 我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用其ifPresent方法对来消费其中的值。

​ 这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。这样使用起来就更加安全了。

​ 例如,以下写法就优雅的避免了空指针异常。

	private static void test1() {
        Author author = getAuthor();
        Optional<Author> optional = Optional.ofNullable(author);
        optional.ifPresent(author1 -> System.out.println(author1.getName()));
    }

4.2.3 获取值

​ 如果我们想获取值自己进行处理可以使用get方法获取,但是不推荐。因为当Optional内部的数据为空的时候会出现异常。
在这里插入图片描述

4.2.4 安全获取值

​ 如果我们期望安全的获取值。我们不推荐使用get方法,而是使用Optional提供的以下方法。

  • orElseGet

    获取数据并且设置数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。

private static void test5() {
        Optional<Author> authorOptional = getAuthorOptional();
        Author author = authorOptional.orElseGet(() -> new Author(5L, "zyy", 22, "纯纯的码农", null));
        System.out.println(author.getName());
    }
  • orElseThrow

    获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。

private static void test6() {
        Optional<Author> authorOptional = getAuthorOptional();
        try {
            Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author为空"));
            System.out.println(author.getName());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

4.2.5 过滤

我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。
 private static void test7() {
        Optional<Author> authorOptional = getAuthorOptional();
        authorOptional
                .filter(author -> author.getAge() > 20)
                .ifPresent(author -> System.out.println(author.getName()));
    }

4.2.6 判断

我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现Optional的好处,**更推荐使用isPresent方法**。
	//判断
    private static void test8() {
        Optional<Author> authorOptional = getAuthorOptional();
        if (authorOptional.isPresent()) {
            System.out.println(authorOptional.get().getName());
        }
    }

4.2.7 数据转换

Optional还提供了mao可以让我们对数据进行转换,并且转换得到的数据也是被Optional包装好的,保证了我们的使用安全。

例如:我们想获取作家的书籍集合。

	//数据转换
    private static void test9() {
        Optional<Author> authorOptional = getAuthorOptional();
        Optional<List<Book>> books = authorOptional.map(author -> author.getBooks());
        books.ifPresent(books1 -> books1.forEach(book -> System.out.println(book.getName())));
    }

5、函数式接口

5.1 概述

只有一个抽象方法的接口我们称之为函数接口。
JDK的函数式接口都加上@FunctionalInterface注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。

5.2 常见函数式接口

  • Consumer 消费接口
    根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数进行消费。
    在这里插入图片描述

  • Function 计算转换接口

    根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数计算或转换,把结果返回
    在这里插入图片描述

  • Predicate 判断接口

根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数条件判断,返回判断结果
在这里插入图片描述

  • Supplier 生产型接口

    根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中创建对象,把创建好的对象返回

    在这里插入图片描述

5.3 常用的默认方法

  • and
    我们在使用Predicate接口的时候可能需要进行判断条件的拼接。而and方法相当于是使用&&来拼接两个判断条件

      例如:打印作家中年龄大于17并且姓名长度大于1的作家
    
private static void test28() {
        //打印作家中年龄大于17并且姓名长度大于1的作家
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(((Predicate<Author>) author -> author.getAge() > 17).and(author -> author.getName().length() > 1))
                .forEach(author -> System.out.println(author.getName() + " : " + author.getAge()));
    }
  • or
    我们在使用Predicate接口时候可能需要进行判断条件的拼接。而or方法相当于是使用||来拼接两个判断条件

    例如:打印作家中年龄大于17或者姓名的长度小于2的作家。
    
  private static void test29() {
        //打印作家中年龄大于17或者姓名的长度小于2的作家。
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(((Predicate<Author>) author -> author.getAge() > 17)
                        .or(author -> author.getName().length() < 2))
                .forEach(author -> System.out.println(author.getAge() + " : " + author.getName()));
    }
  • negate
    Predicate接口中的方法。negate方法相当于是在判断添加前面加了个! 表示取反

    例如:打印作家中年龄不大于17的作家。

 private static void test30() {
        //打印作家中年龄不大于17的作家
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(((Predicate<Author>) author -> author.getAge() > 17).negate())
                .forEach(author -> System.out.println(author.getName() + " : " + author.getAge()));
    }

6、方法引用

我们在使用Lambda时,如果方法体只有一个方法调用的话(包括构造方法),我们可以用方法引用进一步简化代码。

6.1 推荐用法

我们在使用lambda时不需要考虑什么时候用方法引用,用哪种方法引用,方法引用的格式是什么。我们只需要在写完lambda方法发现方法体只有一行代码,并且是方法的调用时使用快捷键尝试是否能够转换成方法引用即可。

​当我们方法引用使用的多了慢慢的也可以直接写出方法引用。

6.2 基本格式

类名或者对象名: :方法名

6.3 语法详解(了解)

6.3.1 引用静态方法

其实就是引用类的静态方法

格式

类名: :方法名

使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。

例如:如下代码就可以用方法引用进行简化

private static void test31() {
        List<Author> authors = getAuthors();
        //优化前
        authors.stream()
                .map(author -> author.getAge())
                .filter(age -> age > 20)
                .map(age -> String.valueOf(age))
                .forEach(s -> System.out.println(s));

        //优化后
        authors.stream()
                .map(Author::getAge)
                .filter(age -> age > 20)
                .map(String::valueOf)
                .forEach(System.out::println);
    }

6.3.2 引用对象的实例方法

格式

对象名 ::方法名

使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这话代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法

例如:

    private static void test32() {
        List<Author> authors = getAuthors();
        StringBuilder sb = new StringBuilder();
        //优化前
        authors.stream()
                .map(author -> author.getName())
                .forEach(name -> sb.append(name));
        
        //优化后
        authors.stream()
                .map(Author::getName)
                .forEach(sb::append);
                
        System.out.println(sb.toString());
    }

6.3.3 引用类的实例方法

格式

类名 : : 方法名

使用前提
​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法。

例如:

public class MethodDemo {

    interface UseString {
        String use(String str, int start, int length);
    }

    public static String subAuthorName(String str, UseString useString) {
        int start = 0;
        int length = 1;
        return useString.use(str, start, length);
    }

    public static void main(String[] args) {
        //优化前
        subAuthorName("三更草堂", new UseString() {
            @Override
            public String use(String str, int start, int length) {
                return str.substring(start,length);
            }
        });
        //优化后
        subAuthorName("三更草堂", String::substring);
    }
}

6.3.4 构造器引用

如果方法体中的一行代码是构造器的话就可以使用构造器引用。
格式

类名 : : new

使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器。

    private static void test33() {
        List<Author> authors = getAuthors();
        //优化前
        authors.stream()
                .map(new Function<Author, String>() {
                    @Override
                    public String apply(Author author) {
                        return author.getName();
                    }
                })
                .map(new Function<String, StringBuilder>() {
                    @Override
                    public StringBuilder apply(String name) {
                        return new StringBuilder(name);
                    }
                })
                .map(new Function<StringBuilder, String>() {
                    @Override
                    public String apply(StringBuilder sb) {
                        return sb.append("三更").toString();
                    }
                })
                .forEach(new Consumer<String>() {
                    @Override
                    public void accept(String name) {
                        System.out.println(name);
                    }
                });

        //优化后
        authors.stream()
                .map(Author::getName)
                .map(StringBuilder::new)
                .map(sb -> sb.append("三更").toString())
                .forEach(System.out::println);
    }

7、高级用法

7.1 基本数据类型优化

我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。

​ 即使我们操作的是整数小数,但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候,你就不能无视这个时间损耗了。

​ 所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。

例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。

private static void test34() {
        List<Author> authors = getAuthors();
        //map
        authors.stream()
                .map(Author::getAge)
                .forEach(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer age) {
                        System.out.println(age);
                    }
                });
        //mapToInt
        authors.stream()
                .mapToInt(author -> author.getAge())
                .forEach(new IntConsumer() {
                    @Override
                    public void accept(int age) {
                        System.out.println(age);
                    }
                });
    }

7.2 并行流

​ 当流中有大量元素时,我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完成。如果我们自己去用代码实现的话其实会非常的复杂,并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话,我们只需要修改一个方法的调用就可以使用并行流来帮我们实现,从而提高效率。

parallel方法可以把串行流转换成并行流

private static void test35() {
        //串行流
        Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        Stream<Integer> stream1 = Stream.of(arr);
        Integer sum1 = stream1
                .filter(num -> num > 5)
                .reduce((result, element) -> result + element)
                .get();
        System.out.println(sum1);

        //并行流
        Stream<Integer> stream2 = Stream.of(arr);
        Integer sum2 = stream2
                .parallel()
                .filter(num -> num > 5)
                .reduce((result, element) -> result + element)
                .get();
        System.out.println(sum2);
        
    }

也可以通过parallelStream直接获取并行流对象。

private static void test36() {
        //串行流对象
        List<Author> authors1 = getAuthors();
        authors1.stream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age -> age > 18)
                .map(age -> age + 2)
                .forEach(System.out::println);

        System.out.println("=========================");
        //并行流对象
        List<Author> authors2 = getAuthors();
        authors2.parallelStream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age -> age > 18)
                .map(age -> age + 2)
                .forEach(System.out::println);
    }
  • 11
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值