函数式编程

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()方法即可

你可能感兴趣的:(java学习笔记,java,jvm,开发语言)