当前位置:   article > 正文

Lambda(Java8函数式编程)_java核心编程 lambda

java核心编程 lambda

一、函数式编程概述

面向对象思想需要关注用什么对象完成什么事情。而函数式编程思想就类似于我们数学中的函数。它主要关注的是对数据进行了什么操作。
优点: 代码简洁,开发快速;接近自然语言,易于理解;易于"并发编程"

二、Lambda表达式

1.概述

Lambda是JDK8中一个语法糖。可以看成是一种语法糖,他可以对某些匿名内部类的写法进行简化。它是函数式编程思想的一个重要体现。让我们不用关注是什么对象。而是更关注我们对数据进行了什么操作。

2.核心原则

可推导可省略。核心就是:只关心参数和具体的函数操作。使用的原则就是:参数是接口,而且只有一个方法需要重写。

3.基本格式

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

4.省略规则

  • 参数类型可以省略
  • 方法体只有一句代码时大括号return和唯―—句代码的分号可以省略
  • 方法只有一个参数时小括号可以省略
  • 以上这些规则都记不住也可以省略不记

5.案例

public static void main(String[] args) {
//        原本启动一个线程的方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("启动一个线程");
            }
        }).start();
    }
    //使用lambda表达式简化后
        new Thread(()-> System.out.println("启动一个线程")).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class Main {
    public static void main(String[] args) {
        //不使用lambda表达式
        foreachArr(new IntConsumer() {
            @Override
            public void accept(int value) {
                System.out.println(value);
            }
        });
        //使用lambda表达式简化后
        foreachArr(a -> System.out.println(a));
    }
    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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
public class Main {
    public static void main(String[] args) {
        //不使用lambda表达式
        int i = calculateNum(new IntBinaryOperator() {
            @Override
            public int applyAsInt(int left, int right) {
                return left + right;
            }
        });
        System.out.println(i);
        //使用lambda表达式简化后
        int k = calculateNum(((left, right) -> left + right));
        System.out.println(k);
    }
    public static int calculateNum(IntBinaryOperator operator) {
        int a = 10;
        int b = 20;
        return operator.applyAsInt(a, b);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

二、Stream流

1.概述

Java8的Stream使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合或数组进行链状流式的操作。可以更方便的让我们对集合或数组操作。

2.准备工作

//作者类
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    private long id;
    private String name;
    private Integer age;
    private String intro;
    private List<Book> books;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
//书籍类
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Book {
    private long id;
    private String name;
    private String category;//分类
    private int score;//评分
    private String intro;//简介
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
//构造数据
public class StreamDemo {
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        System.out.println(authors);
    }
    private static List<Author> getAuthors() {
//数据初始化
        Author author1 = 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, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));


        author1.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);
        List<Author> authorList = new ArrayList<>(Arrays.asList(author1, author2, author3, author4));
        return authorList;

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

3.需求&&实现

需求: 现在需要打印所有年龄小于18的作家的名字,并且要注意去重。
实现: 在上述StreamDemo类中的main方法中实现一下代码

//不使用Lambda表达式
 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.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());
                    }
                });
    }
    //使用Lambda表达式
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //条件过滤
        //打印姓名
        authors.stream()//把集合转化为流
                .distinct()//去重
                .filter(author -> author.getAge()<18)
                .forEach(author -> System.out.println(author.getName()));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在这里插入图片描述

4.常用操作

4.1创建流

单列集合:集合对象.stream()

 List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();
  • 1
  • 2

数组:

//数组:Arrays.stream(数组)或者使用Stream.of来创建
        Integer arr[]={1,2,3,4,5};
        Stream<Integer> stream = Arrays.stream(arr);
        Stream<Integer> stream1 = Stream.of(arr);
  • 1
  • 2
  • 3
  • 4

双列集合:

//        双列集合:转换成单列集合后再创建
        HashMap<String, Integer> map = new HashMap<>();
        map.put("肖邦",12);
        map.put("贝多芬",22);
        map.put("莫扎特",15);
        Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
4.2中间操作

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

   public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印所有姓名长度大于1的作家的姓名
        authors.stream()//把集合转化为流
                .filter(author -> author.getName().length()>1)
                .forEach(author -> System.out.println(author.getName()));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述

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

    public static void main(String[] args) {
        List<Author> authors = getAuthors();
//打印所有作家的姓名
        authors.stream()//把集合转化为流
                .map(author -> author.getName())
                .forEach(author -> System.out.println(author));
                //                .forEach(name -> System.out.println(name));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
把每个人的年龄加10

 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()//把集合转化为流
                .map(author -> author.getAge())
                .map(age->age+10)
                .forEach(author -> System.out.println(author));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

distinct:
可以去除流中的重复的元素。
注意: distinct方法是依赖object的equals方法来判断是否是相同对象的。所以需要注意重写equals方法。


 public static void main(String[] args) {
 //打印所有作家姓名,要求其中不能有重复元素
        List<Author> authors = getAuthors();
        //打印所有姓名长度大于1的作家的姓名
        authors.stream()//把集合转化为流
                .distinct()
                .forEach(author -> System.out.println(author.getName()));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述

sorted:
可以对流中的元素进行排序。注意:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable接口。

 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素。
        authors.stream()//把集合转化为流
                .distinct()
                .sorted((o1, o2) -> o2.getAge()-o1.getAge())
                .forEach(author -> System.out.println(author.getName()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述

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

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

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。
        authors.stream()//把集合转化为流
                .distinct()
                .sorted((o1, o2) -> o2.getAge()-o1.getAge())
                .skip(1)
                .forEach(author -> System.out.println(author.getName()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印所有书籍的名字。要求对重复的元素进行去重。
        authors.stream()//把集合转化为流
                .flatMap(new Function<Author, Stream<Book>>() {
                    @Override
                    public Stream<Book> apply(Author author) {
                        return author.getBooks().stream();
                    }
                })
                .distinct()
                .forEach(new Consumer<Book>() {
                    @Override
                    public void accept(Book book) {
                        System.out.println(book.getName());
                    }
                });
//使用lambda表达式
 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印所有书籍的名字。要求对重复的元素进行去重。
        authors.stream()//把集合转化为流
                .flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream())
                .distinct()
                .forEach(book -> System.out.println(book.getName()) );

    }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

在这里插入图片描述
在这里插入图片描述

4.3终结操作

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

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印这些作家的所出书籍的数目,注意删除重复元素。
        long count = authors.stream()//把集合转化为流
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

max&min:
可以用来或者流中的最值。注意:max和min返回的是Optional,所以不能再max或者min后继续使用链式编程。(即不能在一个Stream中同时求出最大和最小)

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //分别获取这些作家的所出书籍的最高分和最低分并打印。
        //Stream<Author> ->Stream<Book> ->Stream<integer> ->求值
        Optional<Integer> max = authors.stream()//把集合转化为流
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((o1, o2) -> o1 - o2);

        Optional<Integer> min = authors.stream()//把集合转化为流
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .min((score1, score2) -> score1 - score2);
        System.out.println(max.get());
        System.out.println(min.get());

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

collect:
把当前流转换成一个集合。

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //获取一个存放所有作者名字的List集合。
        List<String> collect = authors.stream()//把集合转化为流
                .map(author -> author.getName())
                .collect(Collectors.toList());//使用工具类的方法

        System.out.println(collect);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //获取一个Map集合,mapl的key为作者名,value为List<Book>
        Map<String, List<Book>> collect = authors.stream()//把集合转化为流
                .distinct()
                .collect(Collectors.toMap(new Function<Author, String>() {
                    @Override
                    public String apply(Author author) {
                        return author.getName();
                    }
                }, new Function<Author, List<Book>>() {
                    @Override
                    public List<Book> apply(Author author) {
                        return author.getBooks();
                    }
                }));

        System.out.println(collect);

    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
//使用lambda表达式
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //获取一个Map集合,mapl的key为作者名,value为List<Book>
        Map<String, List<Book>> collect = authors.stream()//把集合转化为流
                .distinct()//转化为map需要两个参数,一个参数告诉如何转化key,另一个如何转化为value
                .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));

        System.out.println(collect);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //判断是否有年龄在29以上的作家
        boolean b = authors.stream()//把集合转化为流
                .anyMatch(author -> author.getAge() > 29);

        System.out.println(b);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //判断是否所有的作家都是成年人
        boolean b = authors.stream()//把集合转化为流
                .allMatch(author -> author.getAge() >= 18);

        System.out.println(b);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
noneMatch:
可以判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false
findAny:
获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素。

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //获取任意一个年龄大于18的作家,如i果存在就输出他的名字
        Optional<Author> authorOptional = authors.stream()//把集合转化为流
                .filter(author -> author.getAge() > 18)
                .findAny();
        //如果有数据就输出,没有就不输出,防止空指针异常
        authorOptional.ifPresent(author -> System.out.println(author.getName()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

findFirst:
获取流中的第一个元素。

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //获取一个年龄最小的作家,并输出他的姓名。
        Optional<Author> authorOptional = authors.stream()//把集合转化为流
                .sorted(((o1, o2) -> o1.getAge()- o2.getAge()))
                .findFirst();
        //如果有数据就输出,没有就不输出,防止空指针异常
        authorOptional.ifPresent(author -> System.out.println(author.getName()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

reduce归并:
对流中的数据按照你指定的计算方式计算出一个结果
(缩减操作)。reduce的作用是把stream中的元素给组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和初始化值进行计算,计算结果再和后面的元素计算。
reduce两个参数的重载形式内部计算模式:
在这里插入图片描述
其中identity就是我们可以通过方法参数传入的初始值,accumulator的apply具体进行什么计算也是我们通过方法参数来确定的。

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //使用reduce求所有作者年龄的和
        Integer sum = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(0, new BinaryOperator<Integer>() {//第一个参数实际上就是我们设置0的参数,第二个是遍历到的参数
                    @Override
                    public Integer apply(Integer result, Integer element) {
                        return result + element;
                    }
                });
        System.out.println(sum);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
//使用lambda表达式
 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //使用reduce求所有作者年龄的和
        //第一个参数实际上就是我们设置0的参数,第二个是遍历到的参数
        Integer sum = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(0, (result, element) -> result + element);
        System.out.println(sum);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //使用reduce求所有作者中年龄的最大值
        //第一个参数实际上就是我们设置的参数,第二个是遍历到的参数
        Integer max = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer result, Integer element) {
                        return result<element?element:result;
                    }
                });
        System.out.println(max);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //使用reduce求所有作者中年龄的最大值
        //第一个参数实际上就是我们设置的参数,第二个是遍历到的参数
        Integer max = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (result, element) -> result<element?element:result);
        System.out.println(max);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
reduce—个参数的重载形式内部的计算:

在这里插入图片描述
案例:

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //使用reduce求所有作者中年龄的最大值(只用一个参数)
        //第一个参数实际上就是数组的第一个数据,第二个是遍历到的参数
        Optional<Integer> maxOptional = authors.stream()
                .map(author -> author.getAge())
                .reduce(new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer < integer2 ? integer2 : integer;
                    }
                });

        maxOptional.ifPresent(age-> System.out.println(age));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5.注意事项

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

三、Optional

1.概述

我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断。
所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。

代码准备:

public class OptionalDemo {
    public static void main(String[] args) {
        Author author = getAuthor();
        System.out.println(author.getName());
    }
    
    public static Author getAuthor(){
        Author author = new Author(1L, "蒙多", 33, "祖安人", null);
        return author ;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.使用

2.1创建对象

Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部。然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常。
我们一般使用Optional静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。

 public static void main(String[] args) {
        Author author = getAuthor();
        Optional<Author> authorOptional = Optional.ofNullable(author);
        authorOptional.ifPresent(author1 -> System.out.println(author1.getName()));
    }
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
如果你确定一个对象不是空的则可以使用Optional的静态方法of来把数据封装成Optional对象。

Author author = new Author();
Optional<Author> authoroptional = Optional.of(author);
//但是—定要注意,如果使用of的时候传入的参数必须不为null。
  • 1
  • 2
  • 3

如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null,这个时候就需要把null封装成Optional对象返回。这时则可以使用Optional的静态方法empty来进行封装。Optional.empty()

2.2安全消费值

我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用其ifPresent方法对来消费其中的值。这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。这样使用起来就更加安全了。
例如,以下写法就优雅的避免了空指针异常。

 Optional<Author> authorOptional = Optional.ofNullable(author);
        authorOptional.ifPresent(author1 -> System.out.println(author1.getName()));
  • 1
  • 2
2.3获取值

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

2.4安全获取值

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

  • orElseGet
    获取数据并且设置数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。
 public static void main(String[] args) {
        Optional<Author> authorOptional = getAuthorOptional();
        Author author = authorOptional.orElseGet(() -> new Author());
        System.out.println(author.getName());
    }
    public static Optional<Author> getAuthorOptional(){
        Author author = new Author(1L, "蒙多", 33, "祖安人", null);
        return Optional.ofNullable(null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • orElseThrow
    获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。
 public static void main(String[] args) throws Throwable {
        Optional<Author> authorOptional = getAuthorOptional();
        Author author = authorOptional.orElseThrow(new Supplier<Throwable>() {
            @Override
            public Throwable get() {
                return new RuntimeException("数据异常");
            }
        });
        System.out.println(author.getName());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

2.5过滤

我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。

public static void main(String[] args)  {
        Optional<Author> authorOptional = getAuthorOptional();
        authorOptional.filter(author -> author.getAge()>23).ifPresent(author -> System.out.println(author.getName()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

2.6判断

我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现optional的好处,更推荐使用ifPresent方法。

2.7数据转换

Optionali还提供了map可以让我们的对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证了我们的使用安全。(和Stream流操作一样)

public static void main(String[] args)  {
        Optional<Author> authorOptional = getAuthorOptional();
        authorOptional.map(author -> author.getBooks()).ifPresent(author -> System.out.println(author));

    }
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

四、函数式接口

1.概述

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

2.常用默认方法

  • and
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印作家中年龄大于17并且姓名的长度大于1的作家。
        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(author -> System.out.println(author.getName()+" "+author.getAge()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • or
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印作家中年龄大于17或者姓名的长度小于2的作家。
        authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()>17;
                    }
                }.or(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getName().length()<2;
                    }
                })).forEach(author -> System.out.println(author.getName()+" "+author.getAge()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • negate
    Predicate接口中的方法。negate方法相当于是在判断添加前面加了个!表示取反
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印作家中年龄不大于17的作家。
        authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()>17;
                    }
                }.negate()).forEach(author -> System.out.println(author.getName()+" "+author.getAge()));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

五、方法的引用

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

1.基本格式

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

2.语法详解

2.1引用类的静态方法

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream().map(author -> author.getAge())
//                .map(age->String.valueOf(age));
                .map(String::valueOf);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
2.2引用对象的实例方法

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        StringBuilder sb = new StringBuilder();
        authors.stream().map(author -> author.getName())
                .forEach(sb::append);
//                .forEach(new Consumer<String>() {
//                    @Override
//                    public void accept(String s) {
//                        sb.append(s);
//                    }
//                });

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
2.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);
            }
        });

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
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("三更草堂", String::substring);

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
2.4构造器引用

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(name->new StringBuilder(name))
                .map(sb->sb.append("浮笙").toString())
                .forEach(str-> System.out.println(str));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(StringBuilder::new)
                .map(sb->sb.append("浮笙").toString())
                .forEach(str-> System.out.println(str));

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最终代码:

 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .map(Author::getName)
                .map(StringBuilder::new)
                .map(sb->sb.append("浮笙").toString())
                .forEach(System.out::println);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

六、高级用法

基本数据类型的优化
我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。
即使我们操作的是整数小数,但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候,你就不能无视这个时间损耗了。
所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。
例如: mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。

 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getAge())
                .map(age->age+10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);
            
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .mapToInt(author -> author.getAge())
                .map(age->age+10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/289797
推荐阅读
相关标签
  

闽ICP备14008679号