开篇

放假前从学校图书馆中借来一本书,Oracle官方的《精通Lambda表达式:Java多核编程》。

假期已过大半才想起来还没翻上几页,在此先推荐给大家。

此书内容及其简洁干练,如果你对Java语法有基础的认识看起来就会不费劲,唯一的缺点就是代码部分的内容以及排版有些错误,不过瑕不掩瑜,无伤大雅。

这个系列就是我对书中每一小节的一个提炼、总结以及实践,每一个知识点我都会附上自己写的代码,这些代码用来验证所学的知识。

才疏学浅,如果有理解错误之处请指正,欢迎交流讨论。

遍历一个集合

最传统的方法大概是用Iterator,当然我比较Low,习惯用i<arr.size()这类的循环。(现在我用for/in,本质上还是Iterator...)

这一类方法叫做外部迭代,意为显式地进行迭代操作,即集合中的元素访问是由一个处于集合外部的东西来控制的,在这里控制着循环的东西就是迭代器。

书中举的例子是pointList,我在这里把它换成一个电话簿。

public class ContactList extends ArrayList<String>{}

里面存储着String类型的联系人。

for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) {
    System.out.println(contactListIterator.next());
}

现在我们将这种遍历方式换成内部迭代。

顾名思义,这种方式的遍历将在集合内部进行,我们不会显式地去控制这个循环。

无需关心遍历元素地顺序,我们只需要定义对其中每一个元素进行什么样的操作。注意在这种设定下可能无法直接获取到当前元素的下标。

Java5中引入Collection.forEach(...),对集合中每一个元素应用其行为参数,这个方法是从其父接口Iterable中继承。

现在我们可以去重写forEach方法。

@Override
public void forEach() {
    for(String s : this) {
        System.out.println(s + " is your contact.");
    }
}

这下我们对电话簿调用forEach方法的时候,遍历操作就会在类的内部完成了。

当然这看起来非常傻并且很不灵活。如果我们想把行为作为参数传给forEach呢?

所幸Java8提供了这种可能,这种行为参数叫做Consumer。

public interface Consumer<T> {
    void accept(T t);
}

一个明显的函数式接口,行为定义在accept方法中。

在定义好了我们自己的Consumer之后,现在这样写:

@Override
public void forEach(Consumer<String> c) {
    for(String s : this) {
        c.accept(s);
    }
}

傻的程度减轻了一些,当然还是不够机智。

在这种情况下一个匿名内部类就能搞定问题(Android里面监听器写到手抽...)

contactList.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s + " is your contact.");
    }
});

这下足够简洁,当然聪明的编译器应该能够推断出我们传入forEach方法的只能是一个Consumer并且需要调用的就是Consumer的唯一方法accept,这段代码还有简化的余地。

所以现在需要登场的就是Lambda表达式了。

既然我们的电话簿ContactList本质上是一个ArrayList<String>,那么编译器也一定能推断出Consumer的类型参数标识为String。

所以这下连参数类型都省了。

contactList.forEach(s -> System.out.println(s + " is your contact, again!"));

->前的p为参数名(即String s中的s),->后为方法体,也可以用一个大括号括起来,因为我这里只写了一句所以就没用。

这就是内部迭代和Lambda相结合的终极奥义了。

现在附上测试代码:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.function.Consumer;

public class ContactList extends ArrayList<String> {
    @Override
    public void forEach(Consumer<? super String> action) {
        super.forEach(action);
    }

    public static void main(String[] args) {
        ContactList contactList = new ContactList();
        contactList.add("Foo");
        contactList.add("Bar");
        contactList.add("Nico");

        for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) {
            System.out.println(contactListIterator.next());
        }

        System.out.println("\n--- Consumer is coming! ---\n");

        contactList.forEach(new ContactAction());
        System.out.println("\n--- Lambda is coming! ---\n");
        contactList.forEach(s -> System.out.println(s + " is your contact, again!"));
    }

    static class ContactAction implements Consumer<String> {
        @Override
        public void accept(String s) {
            System.out.println(s + " is your contact.");
        }
    }
}

以及运行结果:

Foo
Bar
Nico

--- Consumer is coming! ---

Foo is your contact.
Bar is your contact.
Nico is your contact.

--- Lambda is coming! ---

Foo is your contact, again!
Bar is your contact, again!
Nico is your contact, again!

初探Lambda表达式/Java多核编程【0】从外部迭代到内部迭代的更多相关文章

  1. 初探Lambda表达式/Java多核编程【1】从集合到流

    从集合到流 接上一小节初探Lambda表达式/Java多核编程[0]从外部迭代到内部迭代,本小节将着手使用"流"这一概念进行"迭代"操作. 首先何为" ...

  2. 初探Lambda表达式/Java多核编程【2】并行与组合行为

    今天又翻了一下书的目录,第一章在这之后就结束了.也就是说,这本书所涉及到的新的知识已经全部点到了. 书的其余部分就是对这几个概念做一些基础知识的补充以及更深层次的实践. 最后两个小节的内容较少,所以合 ...

  3. 初探Lambda表达式/Java多核编程【3】Lambda语法与作用域

    接上一篇:初探Lambda表达式/Java多核编程[2]并行与组合行为 本节是第二章开篇,前一章已经浅显地将所有新概念点到,书中剩下的部分将对这些概念做一个基础知识的补充与深入探讨实践. 本章将介绍L ...

  4. 初探Lambda表达式/Java多核编程【4】Lambda变量捕获

    这周开学,上了两天感觉课好多,学校现在还停水,宿舍网络也还没通,简直爆炸,感觉能静下心看书的时间越来越少了...寒假还有些看过书之后的存货,现在写一点发出来.加上假期两个月左右都过去了书才看了1/7都 ...

  5. Lambda&Java多核编程-5-函数式接口与function包

    从前面的总结中我们知道Lambda的使用场景是实现一个函数式接口,那么本篇就将阐述一下何为函数式接口以及Java的function包中提供的几种函数原型. 函数式接口 早期也叫作SAM(Single ...

  6. Lambda&Java多核编程-6-方法与构造器引用

    在Lambda&Java多核编程-2-并行与组合行为一文中,我们对Stream<Contact>里的每一位联系人调用call()方法,并根据能否打通的返回结果过滤掉已经失效的项. ...

  7. Lambda表达式和函数式编程

    Lambda表达式和函数式编程 https://www.cnblogs.com/bigbigbigo/p/8422579.html https://www.runoob.com/java/java8- ...

  8. Lambda&Java多核编程-7-类型检查

    本篇主要介绍Lambda的类型检查机制以及周边的一些知识. 类型检查 在前面的实践中,我们发现表达式的类型能够被上下文所推断.即使同一个表达式,在不同的语境下也能够被推断成不同类型. 这几天在码一个安 ...

  9. Lambda 表达式(C# 编程指南) 微软microsoft官方说明

    Visual Studio 2013 其他版本 Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地 ...

随机推荐

  1. myeclipse设置以及快捷键

    http://blog.csdn.net/anxin323/article/details/40214467 如何查看jar包里的源码和doc文档? 1. jar文件右键properties--jav ...

  2. iOS两个关于对象的关键字

    标签: swift新特性(__nullable和__nonnull) 最近在看老师写代码的时候经常遇到两个陌生的关键字,但是当我在我的电脑上敲得时候就是敲不出,后来才知道这是为了swift与OC混编的 ...

  3. Lua学习系列(四)

    lua 资源:http://www.dcc.ufrj.br/~fabiom/lua/ 第一个Lua程序 http://www.dcc.ufrj.br/~fabiom/lua/ 原文:https://w ...

  4. 不同版本的mysql字符集的默认编写

    原来在5.1版本时,为了解决中文乱码问题设置默认字符集为utf8时,在my.ini内的 [mysql] 和 [mysqld] 项中都是写: default-character-set=utf8 到了5 ...

  5. Jquery使用常见(全)

    一.选择器实例 语法 描述 $(this) 当前 HTML 元素 $("p") 所有 <p> 元素 $("p.intro") 所有 class=&q ...

  6. PHP实反向代理-收藏

    需求 现在有些后辍的域名不支持备案,这个时候需要用免备案主机或空间做个反向代理,这样可实现内容存放在国内主机统一管理 实现 用 php-dynamic-mirror 可实现,并在头部进行域名转换,可实 ...

  7. 测试MarsEdit

    测试MarsEdit 今天在MAC上使用MarsEdit编写第一篇博客,测试使用. 今天在MAC上使用MarsEdit编写第一篇博客,测试使用. -(void)myBtnAction:(UIButto ...

  8. IOS开发-UI学习-沙盒机制&文件操作

    ž苹果为软件的运行提供了一个沙盒机制 每个沙盒含有3个文件夹:Documents, Library 和 tmp.因为应用的沙盒机制,应用只能在几个目录下读写文件 žDocuments:苹果建议将程序中 ...

  9. 两台机子的repcached Memcache 的安装与实验

    安装memcached前先要确定系统是否安装了gcc: 1.解压安装包: tar -zxf memcached-1.2.8-repcached-2.2.tar.gz 2.编译: 系统应安装了libev ...

  10. .NET中四种常用事物

    在一个MIS系统中,没有用事务那就绝对是有问题的,要么就只有一种情况:你的系统实在是太小了,业务业务逻辑有只要一步执行就可以完成了.因此掌握事务 处理的方法是很重要,进我的归类在.net中大致有以下4 ...