java8函数式编程学习(一):lambada表达式和stream流的使用

简介

jdk8的新特性中的lambada和stream流的简单使用。

函数式编程

优点

  • 代码的可读性更高,更简洁,开发更快速,易于理解
  • 更高效率的处理大数据量的集合

底层使用了并行流,简化了多线程的用法,不用自己去编写复杂的多线程,而是调用函数式编程的方法,可以高效率的处理大数据集合。

思想

和面向对象不同,不用关注哪个对象完成了什么操作,而是关注对数据做了什么操作,类似于数学中的函数。

lambada表达式

可以对某些匿名内部类进行简化,是函数式编程的一个重要体现,让我们不用关注什么是对象,而是关注数据的操作。

格式

(参数)->{代码}

核心原则是:可推导可省略
如果类型可以推导出来,就省略类型,方法名可以推导出来,就省略方法名。

例子一

public class lambadaTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello,christ");
            }
        }).start();
        //使用lambada表达式编写
        new Thread(()->{
            System.out.println("it's too dark");
        }).start();
    }
}

省略掉的

new Thread(new Runnable() {
            @Override
            public void run() {}}

其实是多余的格式化的代码,类名方法名都是因为Java格式的限制,lambada只关注它的参数和方法体

例子二

package com;

import java.util.function.IntBinaryOperator;

public class lambadaTest2 {
    public static void main(String[] args) {
        int i =calculateNum(new IntBinaryOperator() {
            @Override
            public int applyAsInt(int left, int right) {
                return left + right;
            }
        });
        //使用lambada改造
        int j =calculateNum((int left, int right)->{
                return left + right;
            });
        System.out.println(i);
        System.out.println(j);

    }
    //IntBinaryOperator是一个接口,里面只有一个方法applyAsInt
    public static int calculateNum(IntBinaryOperator operator){
        int a = 10;
        int b = 20;
        return operator.applyAsInt(a,b);
    }
}

对于一个匿名内部类,可以在方法名上alt+回车,查看是否可以替换成lambada表达式。
在这里插入图片描述
对于一个lambada表达式,也可以还原成普通的匿名内部类。
在这里插入图片描述

省略规则

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

例子二中的代码

        int j =calculateNum((int left, int right)->{
                return left + right;
            });

可以省略成

        int j =calculateNum((left,right)->
                 left + right);

Stream流

stream流使用的是函数式编程,可以更方便的对集合或者数组进行链状流式的操作。

例子

创建一个maven项目

pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
</dependencies>
    <groupId>org.example</groupId>
    <artifactId>streamTest</artifactId>
    <version>1.0-SNAPSHOT</version>


</project>
Author类
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author {
    private Long id;
    private String name;
    private Integer age;
    private String intro;
    private List<Book> books;
}
Book类
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author {
    private Long id;
    private String name;
    private Integer age;
    private String intro;
    private List<Book> books;
}
StreamTest类
package com;

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

public class StreamTest1{
    public static void main(String[] args) {
        List<Author> authors  = getAuthors();
        //使用流操作,打印出年龄大于25的作者,名字去重
        authors.stream()
                .distinct()
                .filter(author -> author.getAge()>25)
                .forEach(author -> System.out.println(author.getName()));
    }
    private static List<Author> getAuthors() {
        Author author1 = new Author(1L, "作者1", 23, "简介1", null);
        Author author2 = new Author(2L, "作者2", 24, "简介2", null);
        Author author3 = new Author(3L, "作者3", 27, "简介3", null);
        Author author4 = new Author(3L, "作者3", 27, "简介3", null);
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();
        books1.add(new Book(1L, "书名1", "分类1", 100, "信息1"));
        books1.add(new Book(2L, "书名2", "分类1", 99, "信息2"));

        books2.add(new Book(3L, "书名3", "分类2", 98, "信息3"));
        books2.add(new Book(3L, "书名4", "分类2", 97, "信息4"));
        books2.add(new Book(4L, "书名5", "分类1,分类2", 96, "信息5"));

        books3.add(new Book(5L, "书名6", "分类1,分类2", 95, "信息6"));
        books3.add(new Book(6L, "书名7", "分类3", 94, "信息7"));
        books3.add(new Book(6L, "书名8", "分类1,分类2,分类3", 93, "信息8"));

        author1.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        return new ArrayList<>(Arrays.asList(author1, author2, author3,author4));
    }
}

注意:这个去重distinct是根据author类中的@EqualsAndHashCode去判断的,也就是author4要和author3一模一样,才可以起效,一样的id,一样的书籍著作

输出结果

在这里插入图片描述

idea进行debug

在这里插入图片描述
有专门的stream流工具,可以看到每一步stream流操作的结果。
在这里插入图片描述

常用操作

创建stream流
集合:

集合对象.stream()

package com;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamTest4 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.stream()
        	.forEach(i-> System.out.println(i));
    }
}
数组

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

package com;

import java.util.stream.Stream;

public class StreamTest3 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5,6};
        Stream.of(arr)
      		  .forEach(arr3-> System.out.println(arr3));
    }
}
map集合

先把map集合使用entrySet()方法转换成set对象,然后这个set对象.stream()转换成流对象。

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

public class StreamTest2 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("one","1");
        map.put("two","2");
        map.put("three","3");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        entries.stream()
               .forEach(entry-> System.out.println(entry.getKey()+"----"+entry.getValue()));
    }
}

filter

可以对stream流中的元素进行过滤。

package com;

import java.util.stream.Stream;

public class StreamTest5 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5,6};
        Stream.of(arr)
                .filter(arr3->arr3>3)
                .forEach(arr3-> System.out.println(arr3));
    }
}
map

可以对Stream流中的元素进行计算或转换。
在上面那个author,book的例子中

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

在这里插入图片描述
从debug中,可以看到,stream流中的元素已经从author转变为string

使用map进行计算

    public static void main(String[] args) {
        List<Author> authors  = getAuthors();
        authors.stream()
                .map(age -> age.getAge())
                .map(age->age+10)
                .forEach(author-> System.out.println(author));
                }
distinct

可以去除流中的重复数据,它依赖于object中的equals方法,所以流中的元素的对象要重写equals方法。

package com;
import java.util.stream.Stream;

public class StreamTest6 {
    public static void main(String[] args) {
        Integer[] arr =  {1,1,2,2,3,3,4,4,5,6};
        Stream.of(arr)
                .distinct() 
                .forEach(arr3-> System.out.println(arr3));
    }
}
sorted

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

import java.util.stream.Stream;

public class StreamTest7 {
    public static void main(String[] args) {
        Integer[] arr =  {5,3,1,2,6};
        Stream.of(arr)
                .sorted()
                .forEach(arr3-> System.out.println(arr3));
    }
}

如果比较的是对象,对象要实现comparable接口,重写compareto方法。

limit

可以设置stream的最大长度,超出的部分会被抛弃

package com;

import java.util.stream.Stream;

public class StreamTest8 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        Stream.of(arr)
                .limit(2)
                .forEach(arr3-> System.out.println(arr3));
    }
}

skip

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

package com;

import java.util.stream.Stream;

public class StreamTest9 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        Stream.of(arr)
                .skip(2)
                .forEach(arr3-> System.out.println(arr3));
    }
}

flatmap

map只能把一个对象转换成另一个对象来作为流中的元素,而flatmap可以把一个对象转换成多个对象作为流中的元素
在上面那个author,book的例子中

        List<Author> authors  = getAuthors();
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .forEach(book -> System.out.println(book));

如果是使用map,那只能转换成book的集合
上面 author.getBooks()返回的是一个list对象,而这个匿名类是需要返回stream类型的,所以还需要把list转换成stream流。

而这个会把集合中的每个元素都变成stream流中的元素。
在这里插入图片描述

在上面那个author,book的例子中,输出书籍的分类

authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .flatMap(book->Arrays.stream(book.getCategory().split(",")))
                .forEach(category-> System.out.println(category));
                }

终结操作

使用stream流必须要有终结操作,不然其他的操作都不会执行。使用了流方法之后,stream流就关闭了,不能继续使用链状方式继续调用方法了。

foreach

对stream流中的元素进行遍历操作,通过传入的参数去指定对遍历到的元素进行具体的操作。上面的例子都用到了。

count

可以用来获取stream流中的元素个数。

package com;

import java.util.stream.Stream;

public class StreamTest10 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        long count = Stream.of(arr)
                .count();
        System.out.println(count);
    }
}
max

获得stream流中的最大值

package com;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest11 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        Optional<Integer> max = Stream.of(arr)
                .max((a, b) -> a - b);
        System.out.println(max.get());
    }
}
min

获得stream流中的最小值

package com;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest12 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        Optional<Integer> min = Stream.of(arr)
                .min((a, b) -> a - b);
        System.out.println(min.get());
    }
}

collect

把当前stream流转换成一个集合。
在上面那个author,book的例子中:
获取一个存放所有作者名字的list集合

        List<Author> authors  = getAuthors();
        List<String> nameList = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toList());
        System.out.println(nameList);

获取一个所有书名的set集合

        List<Author> authors  = getAuthors();
        Set<Book> bookSet = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());
        System.out.println(bookSet);

获取一个map集合,map的key是作者的名字,value是List< book>

        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

判断stream流中是否有任意符合匹配条件的元素,返回boolean。

package com;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest13 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        boolean b = Stream.of(arr)
                .anyMatch(a -> a == 1);
        System.out.println(b);
    }
}
allMatch

判断stream流中所有的元素都符合匹配条件,返回boolean。

public class StreamTest14 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        boolean b = Stream.of(arr)
                .allMatch(a -> a > 0);
        System.out.println(b);
    }
}
noneMatch

判断stream流中所有的元素都不符合匹配条件,返回boolean。

package com;

import java.util.stream.Stream;

public class StreamTest15 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        boolean b = Stream.of(arr)
                .noneMatch(a -> a < 0);
        System.out.println(b);
    }
}
findany

获取stream流中的任意一个元素。

package com;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest16 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        Optional<Integer> a = Stream.of(arr)
                .findAny();
        System.out.println(a.get());
    }
}

奇怪的是一直返回1.

findfirst

获取流中的第一个元素。

package com;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest17 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        Optional<Integer> a = Stream.of(arr)
                .findFirst();
        System.out.println(a.get());
    }
}

reduce归并

对stream流中的数据按照指定的计算方式计算出一个结果。会传入一个初始值,这个初始值会和stream流中的元素(循环遍历每一个)进行计算,得到的结果赋值给这个初始值变量,继续下一个元素(下一轮的循环),最后得到一个结果。
它的底层代码是:
在这里插入图片描述

在上面那个author,book的例子中:
计算作者的年龄的总和

        List<Author> authors  = getAuthors();
        Integer sun = authors.stream()
                .map(author -> author.getAge())
                .reduce(0, (result, element) -> result + element);
        System.out.println(sun);

解析:
reduce(0, (result, element) -> result + element)这行代码中的0就是初始值。
有点类似于

package com;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest18 {
    public static void main(String[] args) {
        Integer[] arr =  {1,2,3,4,5};
        int sum = 0;
        for(Integer i:arr){
            sum = sum + i;
        }
        System.out.println(sum);
    }
}
使用reduce求最大值
package com;

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

public class StreamTest19 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(2);
        list.add(3);
        list.add(1);
        Integer min = list.stream()
                .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
        System.out.println(min);
    }
}
传入一个参数的reduce方法

底层的实现是:
在这里插入图片描述
相当于是把stream流中的第一个元素赋值给了result。

stream流的注意事项

  • 如果没有终结操作,流的操作是不会开始执行的
  • 流式一次性的,使用了终结操作以后,是不能再使用了
  • 对stream流里面的元素做了很多的处理操作,但这些都不会影响到原来的集合中的元素的。
  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值