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

简介

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

函数式编程

优点

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

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

思想

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

lambada表达式

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

格式

(参数)->{代码}

核心原则是:可推导可省略

如果类型可以推导出来,就省略类型,方法名可以推导出来,就省略方法名。

例子一

java 复制代码
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();
    }
}

省略掉的

java 复制代码
new Thread(new Runnable() {
            @Override
            public void run() {}}

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

例子二

java 复制代码
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,代码后面的分号都可省略
  • 方法只有一个参数时,小括号可以省略

例子二中的代码

java 复制代码
        int j =calculateNum((int left, int right)->{
                return left + right;
            });

可以省略成

java 复制代码
        int j =calculateNum((left,right)->
                 left + right);

Stream流

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

例子

创建一个maven项目

pom.xml文件
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类
java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author {
    private Long id;
    private String name;
    private Integer age;
    private String intro;
    private List<Book> books;
}
Book类
java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author {
    private Long id;
    private String name;
    private Integer age;
    private String intro;
    private List<Book> books;
}
StreamTest类
java 复制代码
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()

java 复制代码
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(数组对象)

java 复制代码
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()转换成流对象。

java 复制代码
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流中的元素进行过滤。

java 复制代码
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的例子中

java 复制代码
    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进行计算

java 复制代码
    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方法。

java 复制代码
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流中的元素进行排序

java 复制代码
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的最大长度,超出的部分会被抛弃

java 复制代码
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个元素,返回剩下的元素。

java 复制代码
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的例子中

java 复制代码
        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的例子中,输出书籍的分类

java 复制代码
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流中的元素个数。

java 复制代码
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流中的最大值

java 复制代码
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流中的最小值

java 复制代码
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集合

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

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

java 复制代码
        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>

java 复制代码
        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。

java 复制代码
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。

java 复制代码
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。

java 复制代码
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流中的任意一个元素。

java 复制代码
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

获取流中的第一个元素。

java 复制代码
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的例子中:

计算作者的年龄的总和

java 复制代码
        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就是初始值。

有点类似于

java 复制代码
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求最大值
java 复制代码
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流里面的元素做了很多的处理操作,但这些都不会影响到原来的集合中的元素的。
相关推荐
自身就是太阳3 分钟前
如何使用Spring框架来实现一个基于SSM(Spring、SpringMVC、MyBatis)的整合项目
java·开发语言·后端·学习·spring·mybatis
Victory_orsh4 分钟前
本科生如何学习机器学习
学习·机器学习
yxg2012_04_066 分钟前
聪明办法学 Python 第二版.1.学习安排
开发语言·python·学习
后端小肥肠18 分钟前
【Spring Security系列】如何用Spring Security集成手机验证码登录?五分钟搞定!
java·spring boot·后端·spring
龟四崛起36 分钟前
你的绩效是不是常年都是B
java·经验分享·程序人生·职场和发展·职场发展
⠀One0ne36 分钟前
软件设计原则(Java实现/给出正例反例)
java·软件工程
拾木2001 小时前
常见的限流算法
java·开发语言
处处清欢1 小时前
MaintenanceController
java·开发语言
不染_是非1 小时前
Django学习实战篇四(适合略有基础的新手小白学习)(从0开发项目)
数据库·后端·学习·django·web
Niu_brave1 小时前
Python基础知识学习(2)
开发语言·python·学习