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

背景

接触过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 资源下载

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容