函数式编程

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

lambda表达式

概述

jdk8中的语法糖函数式编程思想的体现

原则

可推导可省略

格式

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

例优化多线程启动代码

//匿名内部类写法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程run方法执行");
            }
        }).start();
 //lambda写法
        new Thread(()->{
            System.out.println("run方法执行");
        }).start();

例2

public static void printNum(IntPredicate predicate){//参数为java自带的接口
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        for (int i : arr) {
            if (predicate.test(i)){
                System.out.println(i);
            }
        }
    }

内部类写法

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

lambda写法

 printNum((a)->a%2==0);//输出数组中为偶数的数

例3类型转换

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

内部类写法

Integer integer = typeConver(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.valueOf(s);
            }
        });

lambda写法

Integer integer1 = typeConver((s) -> Integer.valueOf(s));

省略规则

  1. 参数类型可以省略
  2. 方法体只有一句是大括号return和唯一一句代码的分号可以省略
  3. 方法只有一个参数时小括号可以省略

Stream流

概述

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

常用操作

创建流

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

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

数组Arrays.stream(数组)Stream.of(数组)

int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
Stream<int[]> arr1 = Stream.of(arr);

双列集合

Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",2);
map.put("3",3);

Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();

中间操作

filter

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

例如

int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
stream
    .filter(a->a>3)//大于3的符合条件可以留在流中
    .forEach(System.out::println);

map

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

authors.stream()
                .distinct()
                .map(author -> author.getName())//原本存的是author对象全部转换成String类型的name属性值
                .forEach(System.out::println);

distinct

去除流中的重复元素依赖Object的equals方法来判断需要重写equals方法

直接调用没有参数

sorted

可以对流中元素进行排序

authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge()- o1.getAge())//o1代表当前
                .forEach(System.out::println);

如果调用空参的sorted方法需要流中元素实现Comparable接口

有参的sorted方法即把排序方法搬到了参数中

limit

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

authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge()- o1.getAge())
                .limit(2)//限制只有两个元素多余的被抛弃
                .map(Author::getName)
                .forEach(System.out::println);

skip

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

authors.stream()
                .distinct()
                .sorted()
                .skip(1)//跳过第一个
                .forEach(System.out::println);

flatMap

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

authors.stream()//每个author中有一个book集合
                .flatMap(author -> author.getBooks().stream())//获取所有的book对象并整合成新流
                .distinct()
                .forEach(System.out::println);
authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .flatMap(book ->Arrays.stream(book.getCategory().split(",")) )//按照,进行切分并转换成流
                .distinct()
                .forEach(System.out::println);

终结操作

forEach

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

count

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

long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();//获取个数

max&min

可以用来获取流中的最值

authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((o1, o2) -> o1-o2);//需要传入比较器

collect

把流转换成集合

list集合

List<String> collect = authors.stream()
                .map(author -> author.getName())
                .distinct()
                .collect(Collectors.toList());//通过工具类

set集合

Set<Book> collect1 = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());

map集合

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

查找和匹配

anyMatch

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

boolean b = authors.stream()
                .distinct()
                .anyMatch(author -> author.getAge() > 29);//判断是否有年龄大于29的数据
allMatch

可以用来判断是否都符合匹配条件结果为Boolean类型都符合为true否则为false

boolean b = authors.stream()
                .allMatch(author -> author.getAge() > 18);//判断是否所有数据的年龄都大于18
noneMatch

可以判断流中的元素是否都不符合匹配条件都不符合返回true否则false

boolean b = authors.stream()
                .noneMatch(author -> author.getAge() > 100);//判断是否所有数据的年龄都不大于100
findAny

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

 Optional<Author> any = authors.stream()
                .filter(author -> author.getAge() > 18)
                .findAny();//findAny不可以传参数有筛选条件要先执行
findFirst

获取流中的第一个元素

同上

reduce归并

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

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

Integer reduce = authors.stream()
                .map(author -> author.getAge())
                .reduce(0, (result, element) -> result + element);//第一个为初始值后面为要进行的操作
//使用reduce求年龄的最大值
        Integer reduce = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (o1, o2) -> o2 > o1 ? o2 : o1);

reduce一个参数的重载形式

初始值默认为null计算时如果初始值为null则将第一个值赋给初始值否则执行给定计算

authors.stream()
                .map(author -> author.getAge())
                .reduce((o1,o2)->o2>o1?o2:o1);

注意

  1. 没有终结操作流不会执行
  2. 流是一次性的
  3. 不会影响原数据

Optional

用于避免空指针异常

使用

创建对象

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

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

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

如果确定对象不为空则可以使用静态方法of来封装。此时如果传入null会报空指针

如果需要封装null到Optional中可以使用empty方法此方法无参数

安全消费值

使用ifPresent()方法如果值为空则不执行任何操作不为空则执行传入的代码

Optional<Author> author1 = Optional.ofNullable(author);
author1.ifPresent(a-> System.out.println(a.getName()));

获取值

可以使用get方法获取当Optional内的值为空时此方法会报异常

安全获取值

orElseGet

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

Optional<Author> author1 = Optional.ofNullable(author);
Author author2 = author1.orElseGet(() -> new Author());//此处为null的话会新建一个对象返回

orElseThrow

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

Optional<Author> author1 = Optional.ofNullable(author);
Author author3 = author1.orElseThrow(() -> new RuntimeException());//如果为空抛出运行时异常

过滤

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

Optional<Author> author1 = Optional.ofNullable(author);
        author1.filter(a -> a.getAge() > 18).ifPresent(a-> System.out.println(a.getName()));

判断

可以使用isPresent进行是否存在数据的判断如果为空返回值为false如果不为空返回值为true。也可以使用ifPresent方法

数据转换

Optional提供了map方法可以让我们对数据进行转换并且转换得到的数据也还是被Optional包装好的

函数式接口

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

jdk的函数式接口都加了@FunctionalInterface注解进行标识

常见函数式接口

Comsumer接口用于消费

Function计算转换接口

Predicate判断接口

Supplier生产型接口

常用默认方法

and

使用predict接口时可能需要进行判断条件的拼接而and方法相当于使用&&来拼接两个判断条件

匿名内部类写法

//打印作家中年龄大于17并且姓名大于1的作家
        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(System.out::println);

lambda写法

authors.stream()
                .filter(((Predicate<Author>) author -> author.getAge() > 17)
                        .and(author -> author.getName().length()>1))
                .forEach(System.out::println);

or

相当于用||拼接两个条件

authors.stream()
                .filter(((Predicate<Author>)author -> author.getAge()>17).or(author -> author.getName().length()<2))
                .forEach(System.out::println);

negate

表示取反

authors.stream()
                .filter(((Predicate<Author>)author->author.getAge()>17).negate())
                .forEach(System.out::println);

方法引用

使用lambda时如果方法体中只有一个方法的调用的话可以用方法引用简化代码

格式

类名或对象名::方法名

Stream基本数据类型的优化

map方法转换后对象为包装类如后面要进行运算则会执行自动拆箱和装箱可能影响效率

可以使用mapToxxx方法xxx为数据类型flatMap也有类似方法

并行流

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

创建流后调用parallel()方法即可

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6