JAVA-函数式编程( 匿名内部类、lambda、方法引用、Optional)

视频链接:https://www.bilibili.com/video/BV1Gh41187uR/?p=55&spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=b663b486fff8f38ec0d10fa6cc5e56cb

Lambda

案例一:线程

public class LambdaDemo1 {
    public static void main(String[] args) {
        innerClass();
        lambda();
    }

    public static void innerClass(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("innerClass新线程run方法执行");
            }
        }).start();
    }

    public static void lambda(){
        new Thread(()->{
            System.out.println("lambda新线程run方法执行");
        }).start();
    }
}

案例二 :计算

import java.util.function.IntBinaryOperator;

/**
 * @author: hwx
 * @create: 2022-09-28 14:24
 **/
public class LambdaDemo2 {
    public static void main(String[] args) {
        innerClass();
        lambda();
    }
    public static void innerClass(){
        int i =calculateNum(new IntBinaryOperator() {
            @Override
            public int applyAsInt(int left, int right) {
                return left+right;
            }
        });
        System.out.println(i);
    }

    public static void lambda(){
        int i = calculateNum((int left,int right)->{
            return  left+right;
        });
        System.out.println(i);

    }


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

案例三:循环打印

import java.util.function.IntPredicate;

/**
 * @author: hwx
 * @create: 2022-09-28 14:36
 **/
public class LambdaDemo3 {

    public static void main(String[] args) {
        System.out.println("innerClass");
        innerClass();
        System.out.println("lambda");
        lambda();
    }

    public static void innerClass(){
        printNum(new IntPredicate() {
            @Override
            public boolean test(int value) {
                return value%2==0;
            }
        });
    }

    public static void lambda(){
        printNum((value)->{return 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);
            }
        }
    }
}

案例四:类型转换

import java.util.function.Function;

/**
 * @author: hwx
 * @create: 2022-09-28 14:45
 **/
public class LambdaDemo4 {
    public static void main(String[] args) {
        System.out.println("innerClass");
        innerClass();
        System.out.println("lambda");
        lambda();
    }

    public static void innerClass(){
       Integer i = typeConver(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.valueOf(s);
            }
        });
        System.out.println(i);
    }

    public static void lambda(){
       Integer i = typeConver((s)->{return Integer.valueOf(s);});
        System.out.println(i);
    }

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

案例五:循环打印

import java.util.function.IntConsumer;

/**
 * @author: hwx
 * @create: 2022-09-28 15:16
 **/
public class LambdaDemo5 {
    public static void main(String[] args) {
        System.out.println("innerClass");
        innerClass();
        System.out.println("lambda");
        lambda();
    }

    public static void innerClass(){
        foreachArr(new IntConsumer() {
            @Override
            public void accept(int value) {
                System.out.println(value);
            }
        });
    }

    public static void lambda(){
        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);
        }

    }
}

省略规则

  • 参数类型可以省略
  • 方法体只有一句代码时大括号、return、分号可以省略
  • 方法只有一个参数时,小括号可以省略

Stream

准备工作

导入lombok

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

Author类

package com.functional.stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * @author: hwx
 * @create: 2022-09-28 15:52
 **/
@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类

package com.functional.stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * @author: hwx
 * @create: 2022-09-28 15:55
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Book {
    //id
    private Long id;
    //书名
    private String name;
    //分类
    private String category;//哲学,小说
    //评分
    private Integer score;
    // 简介
    private String intro;
}

数据准备

package com.functional.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author: hwx
 * @create: 2022-09-28 16:02
 **/
public class StreamDemo {
    public static void main(String[] args) {
        List<Author> authors = getAuthors();

    }

    public static List<Author> getAuthors(){
        //数据初始化
        Author author1 = new Author(1L,"蒙多",33,"一个从菜刀明悟哲理的祖安人",null);
        Author author2 = new Author(2L,"亚索",15,"狂风也追逐不上他思考的速度",null);
        Author author3 = new Author(3L,"易",16,"剑与灯笼",null);
        Author author3 = new Author(3L,"易",16,"剑与灯笼",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,"风吹不到的地方","哲学与爱情",88,"思维随风走"));
        books2.add(new Book(4L,"吹风","小说",88,"剑技"));
        books2.add(new Book(4L,"吹风","小说",88,"剑技"));

        books3.add(new Book(5L,"你的剑就是我的剑","传记",88,"从菜鸟到高手"));
        books3.add(new Book(5L,"你的剑就是我的剑","传记",88,"从菜鸟到高手"));

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

入门案例

必须要有终结操作(forEach),中间操作才能起作用

   public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //打印年龄小于18的作者名,注意去重
        authors.stream()
                .distinct()//去重
                .filter(author -> author.getAge()<18)//筛选
                .forEach(author -> System.out.println(author.getName()));//遍历
    }

常用操作

创建流

单列集合

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

数组

 Integer []  arr = {1,2,3,4,5,6};
 Stream<Integer> stream = Arrays.stream(arr);
 Stream<Integer> stream2 = Stream.of(arr);

双列集合

 Map<String,Integer> map = new HashMap<>();
  map.put("蜡笔小新",10);
  map.put("悟空",22);
  map.put("鸣人",16);
  map.entrySet().stream()
                .filter(entry -> entry.getValue()>10)
                .forEach(entry -> System.out.println(entry.getKey()+"===="+entry.getValue()));

中间操作

filter
  • 符合条件的才会留在流里面
map
  • 对数据进行计算或转换
 public static void main(String[] args) {
            List<Author> authors = getAuthors();
            authors.stream()
                    .map(author -> author.getName())
                    .forEach(s -> System.out.println(s));
    }
   public static void main(String[] args) {
            List<Author> authors = getAuthors();
            authors.stream()
                    .map(author -> author.getAge())
                    .map(age -> age+10)
                    .forEach(age -> System.out.println(age));
            System.out.println("===原数据不变");
            authors.stream()
                    .forEach(author -> System.out.println(author.getAge()));
    }
distinct
  • 去除流中的重复元素
  • 该方法是依赖Object的equals方法来判断是否是相同对象,所以要注意重写equls方法
sort
  • 对流中的元素进行排序
  • 如果调用的是空参的sort方法,需要流中的元素实现了comparable接口
package com.functional.stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * @author: hwx
 * @create: 2022-09-28 15:52
 **/
@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 this.getAge()-o.getAge();
    }
}

对流中元素进行年龄排序,要求不能有重复元素

  public static void main(String[] args) {
            List<Author> authors = getAuthors();
            authors.stream()
                    .distinct()
                    .sorted()
                    .forEach(author -> System.out.println(author.getAge()));
    }
 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.getAge()));
    }
limit
  • 设置流的最大长度,超出的部分将被抛弃

对流中元素进行年龄排序,要求不能有重复元素,然后打印其中年龄最大的两位的名字

 public static void main(String[] args) {
            List<Author> authors = getAuthors();
            //打印年龄小于18的作者名,注意去重
            authors.stream()
                    .distinct()
                    .sorted(((o1, o2) -> o2.getAge()- o1.getAge()))
                    .limit(2)
                    .forEach(author -> System.out.println(author.getName()));
    }
skip
  • 跳过流中的前n个元素,返回剩下的元素

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

    public static void main(String[] args) {
            List<Author> authors = getAuthors();
            //打印年龄小于18的作者名,注意去重
            authors.stream()
                    .distinct()
                    .sorted(((o1, o2) -> o2.getAge()- o1.getAge()))
                    .skip(1)
                    .forEach(author -> System.out.println(author.getName()));
    
flatMap
  • map只能把一个对象转换成另外一个对象来作为流中的元素,而flatMap可以把一个对象转换成多个对象作为流中的元素

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

 public static void main(String[] args) {
            List<Author> authors = getAuthors();
            //打印年龄小于18的作者名,注意去重
            authors.stream()
                    .flatMap(author -> author.getBooks().stream())
                    .distinct()
                    .forEach(s -> System.out.println(s));
    }

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

  public static void main(String[] args) {
            List<Author> authors = getAuthors();
            //打印年龄小于18的作者名,注意去重
            authors.stream()
                    .flatMap(author -> author.getBooks().stream())
                    .distinct() //对书去重
                    .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                    .distinct() //对分类去重
                    .forEach(category -> System.out.println(category));
    }

终结操作

forEach
  • 对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行扫描具体操作
  public static void main(String[] args) {
            List<Author> authors = getAuthors();
            authors.stream()
                    .forEach(author -> System.out.println(author.getName()));
    }
count
  • 获取流中元素的个数
 public static void main(String[] args) {
        List<Author> authors = getAuthors();
        long count = authors.stream()
                .distinct()
                .count();
        System.out.println(count);
    }
max&min
  • 用来获取流中的最值

获取最大值和最小值

  public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((o1, o2) -> o1 - o2);
        System.out.println(max.get());
        Optional<Integer> min = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .min((o1, o2) -> o1 - o2);
        System.out.println(min.get());
    }
collect
  • 将当前的流转换成一个集合

获取存放作者名的集合(list)

    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        List<String> collect = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toList());
        System.out.println(collect);
    }

获取存放书籍的集合(set)

  public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Set<Book> collect = authors.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());
        System.out.println(collect);
    }

获取作者和他的书(map)

   public static void main(String[] args) {
        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);
    }
查找与匹配
anyMatch
  • 只要流中有任意一个符合条件则返回true

判断是否有大于50岁的作家

    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        boolean b = authors.stream()
                .anyMatch(author -> author.getAge() > 50);
        System.out.println(b);
    }
allMatch
  • 如果流中的元素都符合条件则返回true
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        boolean b = authors.stream()
                .allMatch(author -> author.getAge() > 10);
        System.out.println(b);
    }

findAny
  • 获取流中的任意一个元素,该方法没有办法保证获取的一定是流中的第一个元素
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Optional<Author> any = authors.stream()
                .filter(author -> author.getAge() > 18)
                .findAny();
        //符合条件才执行防止空指针
        any.ifPresent(author -> System.out.println(author.getName()));
    }
findFirst
  • 获取流中符合条件的第一个元素
    public static void main(String[] args) {
        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:初始值
apply方法的具体计算可以自己选择

案例一:计算作家年龄总和

   public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Integer reduce = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(0, ((integer, integer2) -> integer + integer2)); //初始值为0
        System.out.println(reduce);
    }

案例二:获取所有作者中年龄的最大值

  public static void main(String[] args) {
        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);
    }

案例三:获取所有作者中年龄的最小值

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Integer min = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MAX_VALUE, (integer, integer2) -> integer > integer2 ? integer2 : integer);
        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();
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getAge())
                .reduce((integer, integer2) -> integer-integer2);
    }

注意事项

  • 惰性求值(如果没有终结操作,则不会执行中间操作)
  • 流是一次性的(一旦一个流对象经过一次终结操作后,这个流就不能再次使用了)
  • 不会影响原数据

Optional

概述

我们在编写代码时经常会遇到空指针异常,所以我们在很多情况下需要做非空判断。

public class StreamDemo {
    public static void main(String[] args) {
        Author author = getAuthor();
        if (author!=null){
            System.out.println(author.getName());
        }
    }

    public static Author getAuthor(){
        Author author1 = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        return author1;
    }

过多的判断语句会让我们的代码臃肿。JDK8引入了Optional,它可以使我们优雅的写出避免空指针异常的代码。并且在很多函数式编程相关的API中也都使用到它。

创建对象

Optional类似包装类,可以把具体的数据封装到Optional对象内部, 然后我们去使用Optional中封装好的方法操作封装进去的数据。

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

    public static void main(String[] args) {
        Author author = getAuthor();
        Optional<Author> author1 = Optional.ofNullable(author);
        author1.ifPresent(author2 -> System.out.println(author2.getName()));
    }

如果想节省封装数据的代码,可以改造getAuthor(),使其直接返回已经封装好的Optional

  public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        author.ifPresent(author1 -> System.out.println(author1.getName()));
    }

    public static Optional<Author>  getAuthor(){
         Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(author);
        return optional;
    }

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

如果确定一个对象不是空的,则使用Optional的静态方法of来吧数据封装成Optional对象

安全消费值

通常使用Optinal对象的ifPresent方法来消费其中的值,这个方法会判断其数据是或否为空,不为空时才会执行具体的消费代码,这样使用起来就更加安全

    public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        author.ifPresent(author1 -> System.out.println(author1.getName()));
    }

获取值

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

    public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        Author author1 = getAuthor().get();
        System.out.println(author1);
    }

    public static Optional<Author>  getAuthor(){
         Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(null);
        return optional;
    }

安全获取值

Optional提供了以下安全获取数据的方法

  • orElseGet

获取数据并且设置数据为空时的默认值

   public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        Author author1 = getAuthor().orElseGet(() ->new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null));
        System.out.println(author1);
    }

    public static Optional<Author>  getAuthor(){
         Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(null);
        return optional;
    }

  • orElseThrow

如果数据不为空则正常获取数据,否则根据传入的参数来创建异常抛出

   public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        author.orElseThrow(() -> new RuntimeException("数据为空"));
    }

    public static Optional<Author>  getAuthor(){
         Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(null);
        return optional;
    }

过滤

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

 public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        author.filter(author1 -> author1.getAge()>13).ifPresent(author1 -> System.out.println(author1));
    }

    public static Optional<Author>  getAuthor(){
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(author);
        return optional;
    }

判断

isPresent()可以判断数据是否存在,如果为空返回false,否则返回true。但是这种方式并不能体现Optional的优势,更推荐使用ifPresent()

    public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        if (author.isPresent()){
            System.out.println(author.get().getName());
        }
    }

    public static Optional<Author>  getAuthor(){
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(author);
        return optional;
    }

数据转换

Optional提供了map进行数据转换,且转换后的数据仍然是被Optional包装好的,包装了我们的使用安全
获取作家书籍的集合

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

    public static Optional<Author>  getAuthor(){
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(author);
        return optional;
    }
   public static void main(String[] args) {
        Optional<Author> author = getAuthor();
        author.map(author1 -> author1.getBooks())
                .ifPresent(books -> System.out.println(books));
    }

    public static Optional<Author>  getAuthor(){
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀明悟哲理的祖安人", null);
        Optional<Author> optional = Optional.ofNullable(author);
        List<Book> books1 = new ArrayList<>();
        books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学与爱情", 88, "用一把到划分爱情"));
        books1.add(new Book(2L, "一个人不能死在同一把刀下", "哲学", 99, "讲述怎么从失败到死亡"));
        author.setBooks(books1);
        return optional;
    }

函数式接口

概述

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

常见函数式接口

  • Consumer消费接口

根据抽象方法的参数列表和返回值类型。在方法中对传入的参数进行消费

  • Function计算转换接口

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

  • Predicate 判断接口

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

  • Supplier 生产型接口

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

常见的默认方法

我们在使用Predicate接口时可能需要进行判断条件的拼接

  • and

and方法相当于是使用&&来拼接两个判断条件

    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream().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.getAge()+"==="+ author.getName()));
    }
  • or

or方法相当于是使用||来拼接两个判断条件

  • negate

negate方法在想当于在判断面前加!表示取反

    public static void main(String[] args) {
        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()+"==="+ author.getName()));
    }

方法引用

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

推荐用法

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

基本格式

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

语法详解

引用类的静态方法

格式

类名::方法名

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

public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream().map(author -> author.getAge())
                .map(new Function<Integer, Object>() {
                    @Override
                    public Object apply(Integer age) {
                        return String.valueOf(age);
                    }
                });
    }
  public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream().map(author -> author.getAge())
                .map(String::valueOf);
    }

引用对象的实例方法

格式

对象名::方法名

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

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

引用类的实例方法

格式

类名::方法名

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

public class MethodDemo {
    interface UseString{
        String user(String str,int start,int length);
    }


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

    public static void main(String[] args) {
        subAuthorName("方法引用", new UseString() {
            @Override
            public String user(String str, int start, int length) {
                return str.substring(start,length);
            }
        });
    }
}
public class MethodDemo {
    interface UseString{
        String user(String str,int start,int length);
    }


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

    public static void main(String[] args) {
        subAuthorName("方法引用", String::substring);
    }
}

构造器引用

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

格式

类名::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));
    }
    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));
    }

高级用法

基本数据类型优化

之前用到的很多stream方法由于都使用了泛型,所以涉及到的参数和返回值都是引用数据类型。
即使我们操作是整数、小数,但实际使用的是它们的包装类。自动拆装箱是要消耗时间的,特别是在数据量大的情况下我们就不能无视这个时间损耗了。
Stream提供了专门针对基本数据类型的方法
例如:mapToint,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等

   public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .map(new Function<Author, Integer>() {
                    @Override
                    public Integer apply(Author author) {
                        return author.getAge();
                    }
                })
                .map(age->age+10)
                .filter(age -> age>18)
                .map(age -> age+2)
                .forEach(System.out::println);
    }
  public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .mapToInt(new ToIntFunction<Author>() {
                    @Override
                    public int applyAsInt(Author author) {
                        return author.getAge();
                    }
                })
                .map(new IntUnaryOperator() {
                    @Override
                    public int applyAsInt(int age) {
                        return age + 10;
                    }
                })
                .filter(age -> age>18)
                .map(age -> age+2)
                .forEach(System.out::println);
    }

并行流

当流中有大量元素时,可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完成。

  • parallel方法可以把串行流转换为并行流
  • 通过parallelStream方法可以直接获取并行流对象
 public static void main(String[] args) {
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = integerStream
                .peek(num -> System.out.println(num+"线程名:"+Thread.currentThread().getName()))
                .filter(num -> num > 5)
                .reduce((integer, integer2) -> integer + integer2)
                .get();
        System.out.println(sum);
    }
    public static void main(String[] args) {
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = integerStream
                .parallel()
                .peek(num -> System.out.println(num+"线程名:"+Thread.currentThread().getName()))
                .filter(num -> num > 5)
                .reduce((integer, integer2) -> integer + integer2)
                .get();
        System.out.println(sum);
    }
    public static void main(String[] args) {
        Stream<Author> authorStream = getAuthors().parallelStream();
        authorStream
                .filter(author -> author.getAge()>5)
                .peek(author -> System.out.println(author.getName()+"====线程名:"+Thread.currentThread().getName()))
                .forEach(author -> System.out.println(author.getName()));
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值