24.函数式编程和Stream流

面向函数式编程思想

面向过程编程思想 : 凡事必躬亲

面向对象编程思想 : 自己的事情别人做 (懒人思维)

面向函数式编程思想 : 忽略对象,忽略对象的行为声明,更关注对象怎么做

函数式接口

函数式接口 : 普通接口
    要求 : 接口中有且仅有一个抽象方法的接口 --> 函数式接口 (自定义常量,默认方法,静态方法,私有方法 --> 这些成员的数量在函数式接口中没有要求)
        
之前就接触到的函数式接口 :

Runnable 接口 : 线程的任务接口  -> 抽象方法 :void run()
Callable<T> 接口 : 线程带结果的任务接口 -> 抽象方法: V call()     
Comparable<E> 接口 : 绑定比较器接口 -> 抽象方法 : int compareTo(E o) 
Comparator<E> 接口 : 独立比较器接口 -> 抽象方法 : int compare(E o1,E o2)     
FileFilter 接口 : 过滤器接口 -> 抽象方法 :  boolean accept(File pathname)  

注解 : 约束函数式接口的格式
    @FunctionalInterface

Lambda

Lambda : 面向函数式编程的体现
Lambda : 对匿名内部类格式的简化书写

Lambda表达式前提 : 被作用的必须是函数式接口

格式 :

    (方法的形参) -> {
        重写后的方法体;
    }

Lambda的简化格式 :
        1. 重写的方法体有且仅有一句话的时候,可以省略重写方法体的 大括号,语句结尾的分号,return  //要省略就一起省略
        2. 所有重写方法的形参类型可以省略
        3. 如果重写方法的形参数量有且仅有一个,可以省略形参的小括号

方法引用

方法引用: 它是对Lambda表达式的简写

方法引用能够使用的条件:
    1. 必须满足Lambda表达式的条件(必须是函数式接口)
    2. 必须要求重写方法内方法体有且仅有一句代码 --> 严苛
    3. 必须要求重写方法内方法体的这句代码: -> 严苛至极
        a. 对象调方法
        b. 类名调静态方法
        c. 创建对象
        d. 创建数组  
    4. 形参要在以上功能中使用 
标志 : :: 

Lambda表达式是可以简化函数式接口的变量与形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。当Lambda表达式满足一些特殊的情况时,还可以再简化:

方法引用

方法引用的语法格式:

(1)对象名::实例方法 --> 对象调方法

(2)类名::静态方法 --> 类名调用静态方法

说明:

  • ::称为方法引用操作符

  • Lambda表达式的形参列表,全部在Lambda体中使用上了,要么是作为调用方法的对象,要么是作为方法的实参。

  • 在整个Lambda体中没有额外的数据。

构造器引用

(1)当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表。

(2) 当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度

构造器引用的语法格式:

  • 类名::new

  • 数组类型名::new

使用说明

    方法引用的前提:
        1. 必须可以用Lambda
        2. 重写方法内的方法体有且仅有一句
        3. 要求这一句必须是
            对象调方法 : 对象名::方法名;
            类名调静态方法 : 类名::静态方法名;
            创建对象/创建数组 : 类名::new / 数组类型::new

            能用匿名内部类的不一定能用Lambda,能用Lambda的不一定能用方法引用;反之都可以

案例

public class MethodDemo {
    public static void main(String[] args) {
        //匿名内部类的写法
        InterA ia1 = new InterA(){
            @Override
            public void print(String str) {
                System.out.println(str);
            }
        };

        //Lambda表达式的写法
        InterA ia2 = str -> System.out.println(str);
        // System.out-> 对象
        // println(str) -> 方法

        //方法引用的写法 :
        // 基于Lambda做的简化,要求重写方法内有且仅有一句代码且这句代码是
        //要么对象调方法
        //要么类名调用静态方法
        //要么是创建对象
        //要么是创建一个数组

        //对象调方法: 格式 对象::方法名
        InterA ia3 = System.out::println;

        //类名调用静态方法 : 格式 类名::方法名
        InterB ib = Integer::parseInt;
        
        //创建对象 : 格式 类型::new
        //InterC ic = () -> new Date();
        InterC ic = Date::new;
        
        //创建InterD的实现类对象
        //InterD id = length -> new int[length];
        //创建数组对象 格式: 数组类型::new
        InterD id = int[]::new;
    }
}

@FunctionalInterface
interface InterA{
    public abstract void print(String str);
}

@FunctionalInterface
interface InterB{
    public abstract int change(String str);
}

@FunctionalInterface
interface InterC{
    public abstract Date get();
}

@FunctionalInterface
interface InterD{
    public abstract int[] get(int length);
}

Stream流

Stream流 : 关于集合的简化使用方式 -> 流式编程 (流水线)
Stream流 : 对Lambda表达式和方法引用的推广 (Stream流中大量使用了Lambda表达式)    

Stream流初体验

案例需求

按照下面的要求完成集合的创建和遍历

- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一步得到的集合

		//创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<String>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

进工厂方法(进流方法)

1. 集合进流
    单列集合 : Collection<E>接口中有方法 
            -> Stream<E> stream()
    双列集合 : 不能直接进流 -> 先把双列变单列再进流
        a. Set<K> keySet()
        b. Collection<V> values()
        c. Set<Map.Entry<K,V>> entrySet()
        
2. 数组进流 : 
    Arrays 数组操作的工具类
        static Stream<T> stream(任意类型的数组)
        
3. 同一类型的一组数据进流
    Stream<T> 接口中的静态方法 :
        static <T> Stream<T> of(任意个同类型的数据)  

车间方法(中间方法)

当流对象调用完方法后,得到的还是一个流对象 --> 中间方法

过滤车间

Stream<T> filter(Predicate<? super T> predicate)  
    
    Predicate<? super T> : 判断性接口 -> 函数式接口
        有且仅有一个抽象方法 : boolean test(T t)
            重写test方法就是设定 过滤的条件

截取车间和跳过车间

截取车间 : Stream<T> limit(long maxSize)  
    : 只保留老流中前maxSize个元素,并生成新的流对象
    
跳过车间 : Stream<T> skip(long n) 
    : 跳过(不要)老流中前n个元素,并生成新的流对象

去重车间

去重车间 : Stream<T> distinct()   -> 把流中的重复元素直接剔除,保留不重复的元素到新流中

合并车间

Stream 接口的静态方法: 
    static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 

转换车间

<R> Stream<R> map(Function<? super T,? extends R> mapper)  : 把老流中元素的类型从T类型变成R类型,返回的是元素类型是R类型的新流

    Function<T,R> : 转换接口 -> 函数式接口
        抽象方法 : R apply(T t) 
            //重写apply方法的逻辑就是如何把 t 元素转换成 r 元素的逻辑

出工厂方法(出流方法)

当流对象调用完方法后,得到的不是一个流对象 --> 终结方法

遍历出厂

void forEach(Consumer<? super T> action)  :  遍历流中的元素并使用

    Consumer<? super T> : 消费型接口 -> 函数式接口
        抽象方法 : void accept(T t)  
            //重写accept方法的方法体就是在使用 流中的元素

统计出厂

long count() : 统计流中有多少个元素

收集出厂

<R,A> R collect(Collector<? super T,A,R> collector)  
    Collector : 收集器接口 
        -> 要启动collect方法,就需要传入一个Collector的实现类对象
        -> 程序员自己不会实现Collector接口
        -> 程序员借助 Collectors 工具类 :
             收集成List集合
                 static <T> Collector<T,?,List<T>> toList()  //Collectors.toList()
             收集成Set集合
                 static <T> Collector<T,?,Set<T>> toSet()  //Collectors.toSet()
             收集成Map集合
        static Collector<T,?,Map<K,U>> toMap(Function<T,K> key, Function<T,V> value)  
                //Collectors.toMap(如何把流中数据变成键,如何把流中数据变成值)

Stream流综合案例

案例需求

    现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作

    - 男演员只要名字为3个字的前三人
    - 女演员只要姓林的,并且不要第一个
    - 把过滤后的男演员姓名和女演员姓名合并到一起
    - 把上一步操作后的元素作为构造方法的参数创建演员对象 -> String 转换 Actor
    - 把转换后的流收集成List集合并遍历List集合

    演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法

//创建集合
        ArrayList<String> manList = new ArrayList<String>();
        manList.add("周润发");
        manList.add("成龙");
        manList.add("刘德华");
        manList.add("吴京");
        manList.add("周星驰");
        manList.add("李连杰");

		
		ArrayList<String> womanList = new ArrayList<String>();
        womanList.add("林心如");
        womanList.add("张曼玉");
        womanList.add("林青霞");
        womanList.add("柳岩");
        womanList.add("林志玲");
        womanList.add("王祖贤");

public class Actor implements Serializable {
    private static final long serialVersionUID = 5215563480733681684L;
    private String name;

    public Actor() {
    }

    public Actor(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Actor actor = (Actor) o;
        return Objects.equals(name, actor.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public String toString() {
        return "Actor{" +
                "name='" + name + '\'' +
                '}';
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值