美文网首页
用Java DIY 函数式方法—— forEach, find,

用Java DIY 函数式方法—— forEach, find,

作者: 檀木丁 | 来源:发表于2017-03-13 23:54 被阅读657次

    背景

    接触过Kotlin, RxJava, Java 8 Stream, 越发对其中常用方法涉及的原理有点了解了。

    • forEach
    • find
    • filter
    • map
    • flatmap

    知道他们的作用,也知道如何去使用, 但是对其中的大概原理不是很明白。最近熟悉Java8的Stream中提供的几个常用方法。 试着自己用java方式实现了类似java8 Stream中的函数式方法。

    接下来,会有3篇文章介绍,如何不使用java 8 stream特性,来实现函数式常用方法, 通过这三篇文章,也能让读者理解其他函数式语言中的相关方法原理

    • 用Java DIY 函数式方法—— forEach, find, filter
    • 用Java DIY 函数式方法—— map
    • 用Java DIY 函数式方法—— flatmap

    注意

    • 不适合对函数式一点基础都没有的读者
    • DIY实现不是完美的,仅仅是用实例表达函数式方法的理解
    • 这个系列文章不是分析java 8 stream中的方法源码,而是对java 8 stream特性,结合Kotlin, Rxjava之类的理解, 使用纯java的方式实现类似的函数式方法。
    • 需要对java 中的泛型以及Collection有了解
    • 会用到java 8 lambda表达式
    • 要实际代码验证,需要 jdk 1.8

    讲解的模式如下:

    • 给出某个场景
    • 使用 java 8
    • 使用DIY 函数实现

    那就进入主题吧: 用Java DIY 函数式方法—— forEach, find, filter

    DIY 函数式方法

    forEach

    作用: forEach 遍历集合中的每个元素,对每个元素做操作

            /** 需求:
             * 给定一个String集合,然后遍历,打印出集合中每个元素
             */
    

    我X, 这是哪门子需求,直接来个循环不就解决了么?
    大兄弟,莫发脾气,静下心来,慢慢看下。

    1. java 8 Iterable.forEach

    java 8 在Iterable上添加了新方法 forEach, 同时也在Stream类中添加了forEach方法

    List<String> list = Arrays.asList("Hello", "World!");
    list.forEach(new Consumer<String>() {
                @Override
                public void accept(String item) {
                    out.println(item);
                }
            });
    

    为什么这么写,自己查看forEach方法的定义。使用lambda表达式,更简洁

    list.forEach(item -> out.println(item));
    

    从上述可以简单得到,在java中 lambda表达式就是用来替代匿名接口实现的!

    2. java 8 Stream.forEach

    List<String> list = Arrays.asList("Hello", "World!");
    list.stream().forEach(new Consumer<String>() {
                @Override
                public void accept(String item) {
                    out.print(item + " ");
                }
            });
    

    使用lambda表达式,更简洁

    list.stream().forEach(item -> out.print(item + " "));
    

    3. DIY forEach

    简单起见,这里使用函数形式,而不是java 8 stream那样单独类,使用链式调用。
    再看一次

    forEach原理: 遍历集合中的每一个元素,然后对其进行我们想要的操作

    注意其中 2点:

    • 遍历
    • 对每个item都有操作

    所以,需要把forEach方法设计为

        public static <T> void forEach(Collection<T> collection, Action<T> action){
            for(T item: collection){
                action.call(item);
            }
        }
        public interface Action<T> {
            void call(T item);
        }
    

    说明: Action<T> 是泛型接口,其中call对item操作的方法!
    forEach方法2个参数,第一个参数是需要处理的泛型集合, 第二个参数是操作处理。
    并且,具体的对每个item做什么事情,在调用时候传入!
    如何使用?

    List<String> list = Arrays.asList("Hello", "World!");
    forEach(list, new Action<String>() {
                @Override
                public void call(String item) {
                    out.println(item);
                }
            });
    

    说明: 使用很简单,就是打印出List中的每个String, 当然你也可以在call方法中做其他的操作。

    lambda简化

    forEach(list, item -> out.println(item));
    

    接下来,实现我们自己的filter函数, 这个是函数式编程中经常使用的函数

    filter

    作用: 过滤Collection中符合条件的元素集合
    简化描述 T -> R

             /** 需求:
             *过滤出 [1, 2, ... 10]中是偶数的集合, 并打印出来!
             */
    

    1. java 8 stream

    List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    integerList.stream()
                    .filter(new Predicate<Integer>() {
                        @Override
                        public boolean test(Integer integer) {
                            return integer % 2 == 0; //判断条件
                        }
                    })
                    .forEach(out::println);
    

    lambda简化版本

    integerList.stream()
                    .filter(integer -> (integer % 2 == 0))
                    .forEach(out::println);
    

    2. DIY filter

    需要明确 filter 的作用是将 T -> T, 可以理解为将集合 T 转换为 集合 T, 其中后面的是前面的子集!

    注意其中 2点:

    • 遍历
    • 有判断操作

    所以,filter函数设计为:

        public static <T> Collection<T> filter(Collection<T> collection, Perdicate<T> perdicate ) {
            Collection<T> result = new ArrayList<>();
            for(T item : collection){
                if(perdicate.call(item)) {
                    result.add(item);
                }
            }
            return result;
        }
    
        public interface Perdicate<T> { 
            boolean call(T item);  //判断方法
        }
    

    说明: Perdicate<T> 是泛型接口,其中perdicate.call(item)是对item的判断,满足条件加入到result 集合中
    如何使用?

    filter(integerList, new Perdicate<Integer>() {
                @Override
                public boolean call(Integer item) {
                    return item % 2 == 0;
                }
            }).forEach(out::println);
    

    lambda简化:

    filter(integerList, item -> item % 2 == 0).forEach(out::println);
    

    再如, 找出集合["a1", "ab1", "a1", "ab2", "ac"]中全部 “a1”元素, 并打印

    filter(Arrays.asList("a1", "ab1", "a1", "ab2", "ac"), item -> item.equals("a1")).forEach(out::println);
    

    仔细看,逻辑清晰,写法简洁!

    接下来,实现我们自己的find函数, 这个是函数式编程中经常使用的函数

    find

    作用: 找到集合中的第一个元素

    注意, java 8 中提供了2个函数,findFirst, findAny, 为了简单,这里就是选择返回集合第一个元素!

            /** 需求:
             * [1, 2, ... 10]中的第一个元素
             */
    

    1. java 8 stream.findFirst

    List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    out.println(integerList.stream().findFirst().get());
    

    2. DIY find

    public static <T> T find(Collection<T> collection){
            return collection.stream().findFirst().get();// 直接复用 stream 方法
    }
    

    小结

    通过java 8 stream 和 DIY 函数对比,发现在Collection基础上也是可以做到类似stream提供的函数。
    大概揭示, 函数式常用方法的原理!
    以上三个比较好理解,下篇的map,以及flatmap,不是很好理解!
    代码上传到csdn 资源下载

    喜欢,用实际点赞支持我吧! 欢迎留言讨论!

    相关文章

      网友评论

          本文标题:用Java DIY 函数式方法—— forEach, find,

          本文链接:https://www.haomeiwen.com/subject/ipgqnttx.html