169it科技资讯
169it -->


当前位置:  编程技术>java/j2ee

Java函数式编程(四):在集合中查找元素

    来源: 互联网  发布时间:2014-11-08

查找元素

现在我们对这个设计优雅的转化集合的方法已经不陌生了,但它对查找元素却也是无能为力。不过filter方法却是为这个而生的。

我们现在要从一个名字列表中,取出那些以N开头的名字。当然可能一个也没有,结果可能是个空集合。我们先用老方法实现一把。

代码如下:

final List<String> startsWithN = new ArrayList<String>();
for(String name : friends) {
if(name.startsWith("N")) {
startsWithN.add(name);
}
}

这么简单的事件,写了这么多代码,也够啰嗦的了。我们先创建了一个变量,然后把它初始为一个空集合。然后遍历原来的集合,查找那些以指定字母开头的名字。如果找到,就插入到集合里。

我们用filter方法来重构一下上面这段代码,看看它的威力到底如何。

代码如下:

final List<String> startsWithN =
friends.stream()
.filter(name -> name.startsWith("N"))
.collect(Collectors.toList());

filter方法接收一个返回布尔值的lambda表达式。如果表达式结果为true,运行上下文中的那个元素就会被添加到结果集中;如果不是,就跳过它。最终返回的是一个Steam,它里面只包含那些表达式返回true的元素。最后我们用一个collect方法把这个集合转化成一个列表——在后面52页的使用collect方法和Collecters类中,我们会对这个方法进去更深入的探讨。

我们来打印一下这个结果集中的元素:

代码如下:

System.out.println(String.format("Found %d names", startsWithN.size()));

从输出结果很明显能看出来,这个方法把集合中匹配的元素全都找出来了。
代码如下:

Found 2 names

filter方法和map方法一样,也返回了一个迭代器,不过它们也就这点相同而已了。map返回的集合和输入集合大小是一样的,而filter返回的可不好说。它返回的集合的大小区间,从0一直到输入集的元素个数。和map不一样的是,filter返回的是输入集的一个子集。

到现在为止,lambda表达式带来的代码简洁性让我们很满意,不过如果不注意的话,代码冗余的问题就开始慢慢滋长了。下面我们来讨论下这个问题。

lambda表达式的重用

lambda表达式看起来很简洁,实际上一不小心很容易就出现代码冗余了。冗余会导致代码质量低下,难以维护;如果我们想做一个改动,得把好几处相关的代码都一起改掉才行。

避免冗余还可以帮忙我们提升性能。相关的代码都集中在一个地方,这样我们分析它的性能表现,然后优化这一处的代码,很容易就能提升代码的性能。

现在我们来看下为什么使用lambda表达式容易导致代码冗余,同时考虑如何去避免它。

代码如下:

final List<String> friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
final List<String> editors =
Arrays.asList("Brian", "Jackie", "John", "Mike");
final List<String> comrades =
Arrays.asList("Kate", "Ken", "Nick", "Paula", "Zach");
We want to filter out names that start with a certain letter.

我们希望过滤一下某个字母开头的名字。先用filter方法简单地实现一下。
代码如下:

final long countFriendsStartN =
friends.stream()
.filter(name -> name.startsWith("N")).count();
final long countEditorsStartN =
editors.stream()
.filter(name -> name.startsWith("N")).count();
final long countComradesStartN =
comrades.stream()
.filter(name -> name.startsWith("N")).count();

lambda表达式让代码看起来很简洁,不过它不知不觉的带来了代码的冗余。在上面这个例子中,如果想改一下lambda表达式,我们得改不止一处地方——这可不行。幸运的是,我们可以把lambda表达式赋值给变量,然后对它们进行重用,就像使用对象一样。

filter方法,lambda表达式的接收方,接收的是一个java.util.function.Predicate函数式接口的引用。在这里,Java编译器又派上用场了,它用指定的lambda表达式生成了Predicate的test方法的一个实现。现在我们可以更明确的让Java编译器去生成这个方法,而不是在参数定义的地方再生成。在上面例子中,我们可以明确的把lambda表达式存储到一个Predicate类型的引用里面,然后再把这个引用传递给filter方法;这样做很容易就避免了代码冗余。

我们来重构前面这段代码,让它符合DRY的原则吧。(Don't Repeat Yoursef——DRY——原则,可以参看The Pragmatic Programmer: From Journeyman to Master[HT00],一书)。

代码如下:

final Predicate<String> startsWithN = name -> name.startsWith("N");
final long countFriendsStartN =
friends.stream()
.filter(startsWithN)
.count();
final long countEditorsStartN =
editors.stream()
.filter(startsWithN)
.count();
final long countComradesStartN =
comrades.stream()
.filter(startsWithN)
.count();

现在不用再重复写那个lambda表达式了,我们只写了一次,并把它存储到了一个叫startsWithN的Predicate类型的引用里面。这后面的三个filter调用里,Java编译器看到在Predicate伪装下的lambda表达式,笑而不语,默默接收了。

这个新引入的变量为我们消除了代码冗余。不过不幸的是,后面我们就会看到,敌人很快又回来报仇雪恨了,我们再看看有什么更厉害的武器能替我们消灭它们。


    
相关技术文章:
    ▪Java函数式编程(五):闭包

     使用词法作用域和闭包 很多开发人员都存在这种误解,认为使用lambda表达式会导致代码冗余,降低代码质量。恰恰相反,就算代码变得再复杂,我们也不会为了代码的简洁性而在代码质量上做任何妥协,下面我们就会看到。 在前面一个例子中我们已经可以重用lambda表达式了;然而,如果再匹配另外一个字母,代码冗余的问题很快又卷土重来了。我们先来进一步分析下这个问题,然后再用词法作用域和闭包来把它解决掉。 lambda表达式带来的冗余 我们来从friends中过滤出那些以N或者B开头的字母。继续延用上面的那个例......


    ▪Java函数式编程(六):Optional

     选取单个元素 直觉来说选取单个元素肯定会比选取多个要简单得多,不过这里也存在一些问题。我们先看下一般的做法的问题是什么,然后再看下如何用lambda表达式来解决它。 我们先新建一个方法来查找一个以特定字母开头的元素,然后打印出来。 代码如下: public static void pickName( final List<String> names, final String startingLetter) { String foundName = null; for(String name : names) { if(name.startsWith(startingLetter)) { foundName = name; break; } } 这个方法简......


    ▪Java函数式编程(七):MapReduce

     译注:map(映射)和reduce(归约,化简)是数学上两个很基础的概念,它们很早就出现在各类的函数编程语言里了,直到2003年Google将其发扬光大,运用到分布式系统中进行并行计算后,这个组合的名字才开始在计算机界大放异彩(那些函数式粉可能并不这么认为)。本文我们会看到Java 8在摇身一变支持函数式编程后,map和reduce组合的首次亮相(这里只是初步介绍,后续还会有针对它们的专题)。 对集合进行归约 现在为止我们已经介绍了几个操作集合的新技巧了:查找匹配元素,查找单个元素,集合转化。这些操作有一个共同点,它们都是......


 
最新技术文章:
    ▪Java中使用开源库JSoup解析HTML文件实例

     HTML是WEB的核心,互联网中你看到的所有页面都是HTML,不管它们是由JavaScript,JSP,PHP,ASP或者是别的什么WEB技术动态生成的。你的浏览器会去解析HTML并替你去渲染它们。不过如果你需要自己在Java程序中解析HTML文档并查找某些元素,标签,属性或者检查某个特定的元素是否存在的话,那又该如何呢?如果你已经使用Java编程多年了,我相信你肯定试过去解析XML,也使用过类似DOM或者SAX这样的解析器,不过很有可能你从未进行过任何的HTML解析的工作。更讽刺的是,在Java应用中,很少会有需要你去解析HTML文档的时候,这里并不包括Servlet或者其它的Java WEB技术。更糟糕......


    ▪Java函数式编程(一):你好,Lambda表达式

     第一章 你好,lambda表达式! 第一节 Java的编码风格正面临着翻天覆地的变化。 我们每天的工作将会变成更简单方便,更富表现力。Java这种新的编程方式早在数十年前就已经出现在别的编程语言里面了。这些新特性引入Java后,我们可以写出更简洁,优雅,表达性更强,错误更少的代码。我们可以用更少的代码来实现各种策略和设计模式。 在本书中我们将通过日常编程中的一些例子来探索函数式风格的编程。在使用这种全新的优雅的方式进行设计编码之前,我们先来看下它到底好在哪里。 改变了你的思......


    ▪Java函数式编程(二):集合的使用

     第二章:集合的使用 我们经常会用到各种集合,数字的,字符串的还有对象的。它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多。在这章中,我们探索下如何使用lambda表达式来操作集合。我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集合进行合并。 遍历列表 遍历列表是最基本的一个集合操作,这么多年来,它的操作也发生了一些变化。我们使用一个遍历名字的小例子,从最古老的版本介绍到现在最优雅的版本。 用下面的代码我们很容易创建一个不可变的名字的......


 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2017,169IT.COM,E-mail:www_169it_com#163.com(请将#改为@)

浙ICP备11055608号