函数式编程------JDK8新特性

news2024/11/22 16:25:19

函数式编程式jdk 8中的语法糖,在许多地方都有用到,以下是一些优点.

  • 能够看懂公司里的代码
  • 大数量下处理集合效率高
  • 代码可读性高
  • 消灭嵌套地狱

Lamda表达式

lamda表达式是函数是编程的基础,先看一个列子
新建一个线程,参数是匿名类内部类(匿名内部类是一个匿名子类对象。这里使用匿名内部类的)并且重写了类的抽象方法


new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("你知道吗 我比你想象的 更想在你身边");
    }
}).start();

简化后

//省去了类名,和方法名 只保留执行逻辑
new Thread(()->{
    System.out.println("你知道吗 我比你想象的 更想在你身边");
}).start();

lamda表达的简化思路就是不关心参数的类名和方法名,只关心参数和实现逻辑,所以以前是匿名内部类和重写的方法作为参数现在只需要,类似js箭头函数的格式就行了
因为lambda表达式其实简写的都是函数式接口(只有一个抽象方法的接口),这里我们点进去看源码
在这里插入图片描述
函数式接口只有一个抽象接口,所以使用匿名内部类并重写方法做参数的本质只在意这个方法的实现逻辑,而不需要是类名方法名(就只有一个抽象方法,所以作为参数重写的方法就是重写的该抽象方法)
简写规则:省略类名和方法名,如果参数只有一个可以不写(),没有参数必须写(),方法体的逻辑只有一行代码时,方法体的{}可以不写,;也要同时省略,并且如果有返回值,return 也不写
ps:

//return 省略;和{}也是
 filter(item->item.getName)

所以上诉的线程代码列子可以简化为

new Thread(()->
    System.out.println("你知道吗 我比你想象的 更想在你身边")
).start();

Stream 流

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

比如这里举例俩个实体类
Author

@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
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //作品
    private List<Book> books;
}

作者同时和book是典型的一对多的关系
Book

@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;
    }

Stream创建流操作

函数式编程在流中的使用是基于流的,所以第一个api就是创建流,有了流就可以进行链式编程

  1. 单列集合创建流
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<Map.Entry<String, Integer>> stream = map.entrySet().stream();

链式编程

当拥有流以后就可以开器链式编程编程了,这里foreach举例

authors.stream().forEach(item-> System.out.println("遍历每一个数据的name"+item.getName()));

发现了吗,这个和foreach是一样的逻辑

for (Author item : authors){
    System.out.println("遍历每一个数据的name"+item.getName());
}

唯一的不同,只是stream流中使用了lambda来进行简化
foreach的原式:

authors.stream().forEach(new Consumer<Author>() {
    @Override
    public void accept(Author item) {
        
    }
});

如果看不懂的表达式可以先使用匿名内部类重写方法来作为参数,然后在调用
所以在集合/数组中这些函数式接口的参数都是便利的每一个数据,而调用这些链式编程方法的参数就是重写实现的逻辑
在这里插入图片描述
可以看到foreach方法调用后就流就不会返回了,也就是无法进行链式调用了,所以接下来的api,采用中间操作和中止操作进行分类

中间操作

filter

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

例如:

​ 打印所有姓名长度大于1的作家的姓名

        List<Author> authors = getAuthors();
        authors.stream()
                .filter(author -> author.getName().length()>1)
                .forEach(author -> System.out.println(author.getName()));
map

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

例如:

​ 打印所有作家的姓名

        List<Author> authors = getAuthors();

        authors
                .stream()
                .map(author -> author.getName())
                .forEach(name->System.out.println(name));
//        打印所有作家的姓名
        List<Author> authors = getAuthors();

//        authors.stream()
//                .map(author -> author.getName())
//                .forEach(s -> System.out.println(s));

        authors.stream()
                .map(author -> author.getAge())
                .map(age->age+10)
                .forEach(age-> System.out.println(age));
distinct

​ 可以去除流中的重复元素。

例如:

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

        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));

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

sorted

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

例如:

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

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

注意:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable。item1-item2是升序,反之降序

limit

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

例如:

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

        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted()
                .limit(2)
                .forEach(author -> System.out.println(author.getName()));
skip

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

例如:

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

//        打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted()
                .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 -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(category-> System.out.println(category));

3.4.3 终结操作

forEach

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

例子:

​ 输出所有作家的名字

//        输出所有作家的名字
        List<Author> authors = getAuthors();

        authors.stream()
                .map(author -> author.getName())
                .distinct()
                .forEach(name-> System.out.println(name));

count

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

例子:

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

//        打印这些作家的所出书籍的数目,注意删除重复元素。
        List<Author> authors = getAuthors();

        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);
max&min

​ 可以用来或者流中的最值。

例子:

​ 分别获取这些作家的所出书籍的最高分和最低分并打印。
注意:这些工具类排序api都是item1-item2达到他设计本来的目的,反之则是颠倒效果


//        分别获取这些作家的所出书籍的最高分和最低分并打印。
        //Stream<Author>  -> Stream<Book> ->Stream<Integer>  ->求值

        List<Author> authors = getAuthors();
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((score1, score2) -> score1 - score2);

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

这里返回的max,和min是一个optional对象(类似包装类)对数据进行一天个非空处理的包装输出:
在这里插入图片描述

collect

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

例子:

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

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

​ 获取一个所有书名的Set集合。

//        获取一个所有书名的Set集合。
        List<Author> authors = getAuthors();
        Set<Book> books = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());

        System.out.println(books);

​ 获取一个Map集合,map的key为作者名,value为List

//        获取一个Map集合,map的key为作者名,value为List<Book>
        List<Author> authors = getAuthors();

        Map<String, List<Book>> map = authors.stream()
                .distinct()
                .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));

        System.out.println(map);
查找与匹配
anyMatch

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

例子:

​ 判断是否有年龄在29以上的作家

//        判断是否有年龄在29以上的作家
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .anyMatch(author -> author.getAge() > 29);
        System.out.println(flag);
allMatch

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

例子:

​ 判断是否所有的作家都是成年人

//        判断是否所有的作家都是成年人
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .allMatch(author -> author.getAge() >= 18);
        System.out.println(flag);
noneMatch

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

例子:

​ 判断作家是否都没有超过100岁的。

//        判断作家是否都没有超过100岁的。
        List<Author> authors = getAuthors();

        boolean b = authors.stream()
                .noneMatch(author -> author.getAge() > 100);

        System.out.println(b);
findAny

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

例子:

​ 获取任意一个年龄大于18的作家,如果存在就输出他的名字

//        获取任意一个年龄大于18的作家,如果存在就输出他的名字
        List<Author> authors = getAuthors();
        Optional<Author> optionalAuthor = authors.stream()
                .filter(author -> author.getAge()>18)
                .findAny();

        optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
findFirst

​ 获取流中的第一个元素。

例子:

​ 获取一个年龄最小的作家,并输出他的姓名。

//        获取一个年龄最小的作家,并输出他的姓名。
        List<Author> authors = getAuthors();
        Optional<Author> first = authors.stream()
                .sorted((o1, o2) -> o1.getAge() - o2.getAge())
                .findFirst();

        first.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求所有作者年龄的和

//        使用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求所有作者中年龄的最大值

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

        System.out.println(max);

​ 使用reduce求所有作者中年龄的最小值

//        使用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();

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

        //        使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        Optional<Integer> minOptional = authors.stream()
                .map(author -> author.getAge())
                .reduce((result, element) -> result > element ? element : result);
        minOptional.ifPresent(age-> System.out.println(age));

3.5 注意事项

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

流的使用范围

上诉的stream的使用演示都在基于集合或者列表进行的,但是实际上,Stream流并不仅限于列表或数组的使用。Stream流是Java 8引入的一个功能强大的处理数据集合的API。它提供了一种类似于流水线的方式来处理数据,可以对任何支持迭代的数据源进行操作,包括但不限于列表、数组、集合、I/O流等。
Stream流可以从多种数据源创建,例如集合类(如List、Set、Map等)、数组、I/O流、生成器函数等。它提供了一系列中间操作(Intermediate operations)和终端操作(Terminal operations),可以对数据进行过滤、映射、排序、聚合等各种操作,帮助简化代码、提高代码可读性和可维护性。
除了列表和数组,你还可以使用Stream流处理其他数据源。例如,你可以从文件中读取数据并创建一个Stream流来处理文件中的内容,或者使用Stream流处理数据库查询结果,甚至可以使用Stream流处理网络数据流。
下面是一些创建Stream流的常见方法:

1.通过集合类创建流:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();

2.通过数组创建流:

int[] array = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(array);

3.通过文件创建流:

Path path = Paths.get(“path/to/file.txt”);
Stream<String> lines = Files.lines(path);

4.通过生成器函数创建流:

Stream.iterate(0, n -&gt; n + 1)
      .limit(10)
      .forEach(System.out::println);

需要注意的是,Stream流是一次性消费的,一旦进行终端操作,流就会关闭。如果希望对同一批数据进行多个操作,可以使用Stream流的方法链式调用。

Optional

4. Optional

4.1 概述

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

​ 例如:

        Author author = getAuthor();
        if(author!=null){
            System.out.println(author.getName());
        }

​ 尤其是对象中的属性还是一个对象的情况下。这种判断会更多。

​ 而过多的判断语句会让我们的代码显得臃肿不堪。

​ 所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。

​ 并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习造成影响。

4.2 使用

4.2.1 创建对象

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

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

        Author author = getAuthor();
        Optional<Author> authorOptional = Optional.ofNullable(author);

​ 你可能会觉得还要加一行代码来封装数据比较麻烦。但是如果改造下getAuthor方法,让其的返回值就是封装好的Optional的话,我们在使用时就会方便很多。

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

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

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

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

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

		Optional.empty()

​ 所以最后你觉得哪种方式会更方便呢?ofNullable

4.2.2 安全消费值

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

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

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

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());

        authorOptional.ifPresent(author -> System.out.println(author.getName()));

4.2.3 获取值

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

4.2.4 安全获取值

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

  • orElseGet

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

            Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
            Author author1 = authorOptional.orElseGet(() -> new Author());
    
  • orElseThrow

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

            Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
            try {
                Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author为空"));
                System.out.println(author.getName());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
    

4.2.5 过滤

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

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
        authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));

4.2.6 判断

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

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());

        if (authorOptional.isPresent()) {
            System.out.println(authorOptional.get().getName());
        }

4.2.7 数据转换

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

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

    private static void testMap() {
        Optional<Author> authorOptional = getAuthorOptional();
        Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());
        optionalBooks.ifPresent(books -> System.out.println(books));
    }

5. 函数式接口

5.1 概述

只有一个抽象方法的接口我们称之为函数接口。

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

5.2 常见函数式接口

  • ​ Consumer 消费接口

    根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数进行消费。

  • ​ Function 计算转换接口

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

  • ​ Predicate 判断接口

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

  • ​ Supplier 生产型接口

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

5.3 常用的默认方法

  • and

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

    例如:

    打印作家中年龄大于17并且姓名的长度大于1的作家。

            List<Author> authors = getAuthors();
            Stream<Author> authorStream = authors.stream();
            authorStream.filter(new Predicate<Author>() {
                @Override
                public boolean test(Author author) {
                    return author.getAge()>17;
                }
            }.and(new Predicate<Author>() {
                @Override
                public boolean test(Author author) {
                    return author.getName().length()>1;
                }
            })).forEach(author -> System.out.println(author));
    
  • or

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

    例如:

    打印作家中年龄大于17或者姓名的长度小于2的作家。

    //        打印作家中年龄大于17或者姓名的长度小于2的作家。
            List<Author> authors = getAuthors();
            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()));
    
  • negate

    Predicate接口中的方法。negate方法相当于是在判断添加前面加了个! 表示取反

    例如:

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

    //        打印作家中年龄不大于17的作家。
            List<Author> authors = getAuthors();
            authors.stream()
                    .filter(new Predicate<Author>() {
                        @Override
                        public boolean test(Author author) {
                            return author.getAge()>17;
                        }
                    }.negate()).forEach(author -> System.out.println(author.getAge()));
    

6. 方法引用

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

6.1 推荐用法

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

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

6.2 基本格式

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

6.3 语法详解(了解)

6.3.1 引用类的静态方法

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

格式
类名::方法名
使用前提

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

例如:

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

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        
        authorStream.map(author -> author.getAge())
                .map(age->String.valueOf(age));

注意,如果我们所重写的方法是没有参数的,调用的方法也是没有参数的也相当于符合以上规则。

优化后如下:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();

        authorStream.map(author -> author.getAge())
                .map(String::valueOf);

6.3.2 引用对象的实例方法

格式
对象名::方法名
使用前提

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

例如:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        StringBuilder sb = new StringBuilder();
        authorStream.map(author -> author.getName())
                .forEach(name->sb.append(name));

优化后:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        StringBuilder sb = new StringBuilder();
        authorStream.map(author -> author.getName())
                .forEach(sb::append);

6.3.4 引用类的实例方法

格式
类名::方法名
使用前提

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

例如:

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

	}

优化后如下:

    public static void main(String[] args) {

        subAuthorName("三更草堂", String::substring);

    }

6.3.5 构造器引用

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

格式
类名::new
使用前提

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

例如:

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

优化后:

        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(StringBuilder::new)
                .map(sb->sb.append("-三更").toString())
                .forEach(str-> System.out.println(str));

7. 高级用法

基本数据类型优化

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

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

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

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

    private static void test27() {

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

        authors.stream()
                .mapToInt(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1021136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web应用系统的小安全漏洞及相应的攻击方式

写作目的 本文讲述一个简单的利用WebAPI来进行一次基本没有破坏力的“黑客”行为。 主要目的如下&#xff1a; 了解什么叫安全漏洞 知道什么是api 了解一些获取api的工具 通过对API的认识了解白盒接口测试基本概念和技术 免责声明&#xff1a; 本文主要是以学习交流为目的&a…

python抠图(去水印)开源库lama-cleaner入门应用实践

1. 关于 Lama Cleaner Lama Cleaner 是由 SOTA AI 模型提供支持的免费开源图像修复工具。可以从图片中移除任何不需要的物体、缺陷和人&#xff0c;或者擦除并替换&#xff08;powered by stable diffusion&#xff09;图片上的任何东西。 特征&#xff1a; 完全免费开源&am…

axios取消上一页面的请求

请求拦截 声明变量用于存放请求请求拦截存放请求导出请求 导航守卫 导入请求集合路由前置守卫中遍历取消所有请求 登录失效取消后续请求

Go学习视频整理(总共59门课程,860GB)

由阿里P8 Golang架构师亲自精心筛选整理的全网最新最具价值的Golang进阶学习课程&#xff01; 培训机构原版教程&#xff01; 课程知识点和一线大厂完美匹配&#xff01; 所有课程资源完整成套&#xff0c;不残缺&#xff0c;不拼凑&#xff0c;不拆开乱发&#xff01; 这系…

深度学习——线性神经网络一

深度学习——线性神经网络一 文章目录 前言一、线性回归1.1. 线性回归的基本元素1.1.1. 线性模型1.1.2. 损失函数1.1.3. 解析解1.1.4. 随机梯度下降1.1.5. 用模型进行预测 1.2. 向量化加速1.3. 正态分布与平方损失1.4. 从线性回归到深度网络 二、线性回归的从零开始实现2.1. 生…

【每日运维】文件系统损坏:shutting down filesystem

问题场景&#xff1a;服务器突发断电导致 处理步骤 先确认属于哪个逻辑卷&#xff1a;ls -l /dev/mapper 通过文件系统命令进行修复&#xff1a;xfs_repair -L /dev/mapper/centos-root 最终修复的预期效果如下&#xff1a; 重启服务器后验证是否正常进入系统

Polygon L2扩容方案揭秘

1. 引言 前序博客&#xff1a; Polygon生态 以太坊扩容是一个几乎与以太坊本身一样古老的问题。扩容任务的复杂性是 以太坊伟大之处的直接结果&#xff1a; 以太坊是有活力的协议&#xff0c;其缓慢进化&#xff0c;以确保链的安全性和去中心化。 当活动的增加刺激了更大的…

vue3项目学习一:创建vue3项目

创建vue3项目 一、使用vue-cli创建vue3项目1.安装vue-cli2.创建vue3项目 二、初始化项目结构三、导入element-ui 一、使用vue-cli创建vue3项目 1.安装vue-cli 先查看是否安装vue-cli 在cmd窗口输入vue -V查看版本&#xff0c;如果出现 则说明存在vue-cli,如果出现 则需要安…

FTP这么“好用”和“便宜”,为什么企业还要替换掉?

FTP是一种历史悠久的网络协议&#xff0c;自1971年问世以来&#xff0c;它因其简易性、便捷性以及强大的跨平台兼容性而被广泛使用。在网站开发、软件更新和数据备份等多个场景中&#xff0c;FTP都发挥了重要作用。不过&#xff0c;随着互联网技术的不断发展和企业需求的多样化…

DockerCompose和Docker镜像仓库

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

SpringCloud Alibaba - Nacos

1.安装与部署 Nacos是阿里巴巴开源的服务注册与发现、配置管理的组件&#xff0c;相当于是EurekaConfig的组合。 Nacos服务器是单独安装部署的&#xff0c;需要下载Nacos服务端程序&#xff0c;下载地址https://github.com/alibaba/nacos。 window下双击startup.cmd 登录Naco…

Linux的Redis集群搭建-主从集群哨兵模式

上次教大家在linux中安装单机版本的redis&#xff1a; Linux安装Redis&#xff08;图文解说详细版&#xff09; 这次我们讲一下Linux安装redis的集群版本 文章目录 &#x1f334;准备redis环境&#x1f334;第一步&#xff0c;下载redis&#x1f334;第二步&#xff0c;传输到…

PCIE研究-2

PCIe是一种高速串行总线&#xff0c;用于连接计算机内部的各种设备。在PCIe中&#xff0c;有四种不同的设备类型&#xff1a;Switch、Bridge、Root Complex和EndPoint。本篇文章将介绍这四种设备类型的基础知识。 1. Switch Switch是PCIe中最常见的设备类型之一&#xff0c;它…

【LeetCode热题100】--11.盛最多水的容器

11.盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 **说明&#xff1a;*…

【线下培训】上海临港: RT-Thread × 瑞萨 工业监视器 RA6M3 HMI Board解决方案

注册RT-Thread官方论坛&#xff0c;即可第一时间获得最新消息&#xff01;更有大量活动赚取积分&#xff0c;免费兑换开发板&#xff01; 注册地址&#xff1a;https://www.rt-thread.org/account/user/register.html RT-Thread 与瑞萨将于10月14日在上海临港举行一场线下培训…

java_web的框架分析

文章目录 本阶段技术体系用项目理解原理controllersClassPathXmlApplicationContextDispatcherServletFruitServiceImplFilter 本阶段技术体系 用项目理解原理 项目的目录 首先设置一个参数&#xff0c;这里里面用反射机制&#xff0c;获取方法的时候如果不设置会获取到arg[0…

AI绘画关键词:小龙女

a lady dressed in a white gown stand beside a dragon, in the style of peter gric, traditional essence, kazuki takamatsu, andreas rocha, life-like avian illustrations, serene faces, aurorapunk,3D --ar 9:16 --s 250 --v 5.2

浅述数据中心供配电系统解决方案及产品选型

安科瑞 华楠 【摘 要】现如今&#xff0c;社会主要领域已从对单个设备的关注转化为对于系统解决方案的关注&#xff0c;数据中心的供应商们也想尽办法去满足所面对的各方面需求。基于此&#xff0c;主要提出了云计算数据中心供配电解决方案&#xff0c;同时还对数据中心供配电…

中小型教育机构这样做,让你轻松抓住受众注意力

教育一直都是家长对于孩子最关心的事情&#xff0c;对于部分家庭来说&#xff0c;教育支出占整个家庭支出的50%左右。 而软文作为目前效果比较明显而且性价高的推广方式&#xff0c;也很适合教育培训行业&#xff0c;因为它能让潜在客户可以清楚地了解产品的特性&#xff0c;感…

乐观善良的属马人,这几年的运势怎么样?

生肖马的人是一个乐观向上&#xff0c;拥有对生活的热情态度&#xff0c;更是个实打实过日子的人&#xff0c; 品性善良&#xff0c;对朋友尽心尽力&#xff0c;在朋友的面前没有丝毫的不真诚&#xff0c; 且乐于助人&#xff0c;因此朋友多&#xff0c;贵人也多。 属马人精力充…