java8之Lambda知识回顾

前提

18年第一篇文章,祝各位新年快乐哈~

介绍Lambda之前,可以看下我们定义View点击事件的一般做法

  1. new View(this).setOnClickListener(new View.OnClickListener() {
  2.          @Override
  3.         public void onClick(View view) {
  4.              //...
  5.          }
  6.      });

这种回调模式在日常开发中很常见,但是随之也暴露了一些不良的弊端
1、非抽象化,整个代码有五行,其实实际逻辑只有在onclick里面,其他并不关心
2、无法使用外部非final属性(这个遇到最多了)
3、this指针意义不明确
4、等等

所以,java8特性Lambda正好解决了这一系列问题,以下

new View(this).setOnClickListener(view->{
            //.. doing
        });

1.引入的lambda

lambda是在java8引入的函数表达式,其实是一种匿名的表达方式,Lambda表达式语法由参数列表->函数体组成。函数体既可以是一个表达式也可以是一个代码块。
例如以下

 # 表达式
()->8; // 这里8是return的返回结果,简化了return
#代码块
()->{
  //... 函数体
}

2.案例

说多不如做多,现在举个🌰

2.1 线程利用lambada表达式
new Thread(()->{
            //这里是线程运行的区域,实质是抽象化了Runnable的run方法
            Log.i(TAG,"Thread is running...");
}).run();
2.2 多种情况利用lambada表达式

首先定义一个测试回调接口类

/**
     * 这个类完全提供一些接口测试
     */
    static class LambdaProvider{

        TestInterface0 mTestInterface0; // 测试不带参数与不带返回值
        TestInterface1 mTestInterface1;// 测试带返回值
        TestInterface2 mTestInterface2;// 测试待返回值、带参数
        TestInterface3 mTestInterface3;//测试抛出异常方法

        public void setmTestInterface0(TestInterface0 mTestInterface0) {
            this.mTestInterface0 = mTestInterface0;
        }

        public void setmTestInterface1(TestInterface1 mTestInterface1) {
            this.mTestInterface1 = mTestInterface1;
        }

        public void setmTestInterface2(TestInterface2 mTestInterface2) {
            this.mTestInterface2 = mTestInterface2;
        }

        public void setmTestInterface3(TestInterface3 mTestInterface3) {
            this.mTestInterface3 = mTestInterface3;
        }

        public interface TestInterface0{
            // 不带参数、不带返回值
            public void method();
        }

        public interface TestInterface1{
            // 带返回值
            public int method1();
        }

        public interface TestInterface2{
            // 带参数
            public int method2(int x);
        }

        public interface TestInterface3{
            // 抛出异常方法
            public void method3() throws Exception;
        }

    }

测试代码

        String value="";
        LambdaProvider mLambdaProvider = new LambdaProvider();
        mLambdaProvider.setmTestInterface1(()->2); // 直接返回2
        mLambdaProvider.setmTestInterface1(()-> 2+4 ); // 直接返回计算表达式
        mLambdaProvider.setmTestInterface1(()->{ // 直接返回作用域
            Log.i(TAG,"setmTestInterface1 here...");
            //... do something
            String str = value; // 不用final就可以访问到了
            this.demo(); // this指针不再模糊
            return 3;
        });

        // # 3 针对参数+返回值利用lambada表达式
        mLambdaProvider.setmTestInterface2((x)->2); // 直接返回2
        mLambdaProvider.setmTestInterface2((x)-> 2+4 ); //x 直接返回计算表达式
        mLambdaProvider.setmTestInterface2((x)->{ // 使用形参x计算后返回
            return x+2+4;
        });

        // #4 针对会抛出异常
        mLambdaProvider.setmTestInterface3(()->{
            throw new IllegalStateException("hi~");
        });

3.高效的 Stream api

如果应用最小支持sdk24以上,Stream是非常高效的事情。当然国内sdk24以上的并不是占绝大多数,但是我们也可以先了解高效的Stream。
先小试牛刀,一般foreach遍历list,我们一般会这么做

        List<String> listSteam = Arrays.asList("0","1","2");
        for (String i:listSteam){
            //...
        }

利用steam api,我们可以这么做

       listSteam.forEach(i->{
            //...
        });

反正我是佩服的😢

什么是Stream?

借鉴某大神的说明,“Stream不是集合元素,它也不是数据结构、不能保存数据,它更像一个更高级的Interator。Stream提供了强大的数据集合操作功能,并被深入整合到现有的集合类和其它的JDK类型中。流的操作可以被组合成流水线(Pipeline)”

使用Stream可以做很多事情,如果以后会接触到rxjava,你会发现很多操作符是借鉴了Stream里面流的概念。在Stream操作中,都会涉及一个步骤

上流(Stream)->处理->下流结果(Stream)

举个例子🌰
首先创建一个测试model,并且实例了一个数组

class Model{
            public String type;//"1","2" 用于区别类型
            public String name;
            public Model(String type, String name) {
                this.type = type;
                this.name = name;
            }

            @Override
            public String toString() {
                return "Model (type: " + type +" name: "+name+")";
            }
        }

 List<LambdaProvider.Model> listSteam = Arrays.asList(new LambdaProvider.Model("1","siven0"),
                new LambdaProvider.Model("2","siven1"),new LambdaProvider.Model("2","siven2")
                ,new LambdaProvider.Model("2","siven3"));

场景1:

只提取type是2的数据到新数组

  List<LambdaProvider.Model> newList = listSteam.stream()
                                                .filter(s->s.type.equals("2"))
                                                .collect(Collectors.toList());
  newList.forEach(s-> consoleInput(TAG,"stream newList: "+s));

Collectors是负责接收上面传递过来的流,tolist是将传递过来的流重新组合成list
filter是负责过滤上面传递过来的流,里面return是一个布尔值

场景2:

提取ype是2的数据,并且将流转化为其他数据的流后组成新的数组

List<String> listIndex = listSteam.stream()
                                  .filter(s->s.type.equals("2"))
                                  .map(s->s.name)
                                  .collect(Collectors.toList());
        listIndex.stream().forEach(s-> consoleInput(TAG,"stream listIndex: "+s));

map组要做流转化,上面的场景是接收到Model的流后,通过访问实体的对象name转化为字符串流(下面用map我会贴更加详细的说明)

map的各种玩法(建议阅读):

        // 玩玩map转化
        List<String> stringList = Arrays.asList("1","2","3"); // 随便申请一个list

        consoleInput(TAG,"玩玩map转化 ----- flatmap ");
        List<String> tmp0  =  stringList.stream()
                .flatMap((s)->{
                    String str = (String) s + " - 转化(flatmap)";
                    consoleInput(TAG,"转化前 "+s + ",转化后 " + str);
                    List<String> result = new ArrayList<>();
                    result.add(str);
                    return result.stream();
                })
//                  为了方便读者阅读,我用非lambda表达式
//                  .flatMap(new Function<String, Stream<String>>() {
//                      @Override
//                      public Stream<String> apply(String s) {
//                          String str = (String) s + " - 转化";
//                          consoleInput(TAG,"转化前 "+s + "转化后 " + str);
//                          List<String> result = new ArrayList<>();
//                          result.add(str);
//                          return result.stream();
//                      }
//                  })
                .collect(Collectors.toList());// 流收集起来
        consoleInput(TAG,"转换后结果输出 ");
        tmp0.stream().forEach(c->consoleInput(TAG,"-> "+c));

        consoleInput(TAG,"玩玩map转化 ----- map ");
        tmp0 = stringList.stream()
                .map((s)->{
                    String str = (String) s + " - 转化(map)";
                    consoleInput(TAG,"转化前 "+s + ",转化后 " + str);
                    return str;
                })
//                  为了方便读者阅读,我用非lambda表达式
//                .map(new Function<String, String>() {
//                    @Override
//                    public String apply(String s) {
//                        String str = (String) s + " - 转化(map)";
//                        consoleInput(TAG,"转化前 "+s + ",转化后 " + str);
//                        return str;                    }
//                })
                .collect(Collectors.toList());// 收集
        consoleInput(TAG,"转换后结果输出 ");
        tmp0.stream().forEach(c->consoleInput(TAG,"-> "+c));

        consoleInput(TAG,"玩玩map转化 ----- sum ");
        int sum = stringList.stream()
                .mapToInt(s->Integer.valueOf(s)) // map转int
                .sorted() // 排序
                .sum();
        consoleInput(TAG,"转换后结果输出 "+sum);

这里要思考的就是flatMap与map有什么区别了,从方法看flatMap是object->stream<T>。而map是object->object。如果你的场景是希望自己的上流传递有多次处理逻辑,并不希望马上得到下流的结果。那flatMap更满足你需求了。如果是直接A到B,明确的结果,map也许更适合你

4.总结

年终了~祝大家工作顺利,事业进步!
by siven

2018.1.5

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,145评论 1 2
  • 第一章 为什么要关心Java 8 使用Stream库来选择最佳低级执行机制可以避免使用Synchronized(同...
    谢随安阅读 1,441评论 0 4
  • Streams 原文链接: Streams 原文作者: shekhargulati 译者: leege100 状态...
    忽来阅读 5,466评论 3 32
  • 原文地址: http://cr.openjdk.java.net/~briangoetz/lambda/lambd...
    mualex阅读 573评论 0 0