当前位置: 首页 > 编程笔记 >

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

包德业
2023-03-14
本文向大家介绍Java函数式编程(四):在集合中查找元素,包括了Java函数式编程(四):在集合中查找元素的使用技巧和注意事项,需要的朋友参考一下

查找元素

现在我们对这个设计优雅的转化集合的方法已经不陌生了,但它对查找元素却也是无能为力。不过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函数式编程(二):集合的使用,包括了Java函数式编程(二):集合的使用的使用技巧和注意事项,需要的朋友参考一下 第二章:集合的使用 我们经常会用到各种集合,数字的,字符串的还有对象的。它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多。在这章中,我们探索下如何使用lambda表达式来操作集合。我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集

  • 假设有几个数组: 我需要找出所有可能的元素集合(1,2,3,4,5...)中的每一个在至少两个阵列(A,B,C....)并以下列方式显示它们: 实际输入是包含字符串的文件。可能有数千个文件,每个文件可能包含一百多个密钥字符串。 我尝试了下面的方法:首先,我通过比较所有可能的数组对来生成元素集。然后,我试图通过使用逻辑生成其他集合——元素集合的交集在数组集合的并集中很常见。像这样: 从上面我们可以得

  • 本文向大家介绍Scala函数式编程专题--scala集合和函数,包括了Scala函数式编程专题--scala集合和函数的使用技巧和注意事项,需要的朋友参考一下 前情提要: Scala函数式编程专题—— 函数式思想介绍 scala函数式编程专题——scala基础语法介绍 前面已经稍微介绍了scala的常用语法以及面向对象的一些简要知识,这次是补充上一章的,主要会介绍集合和函数。 注意噢,函数和方法是

  • 本文向大家介绍Java函数式编程(十):收集器,包括了Java函数式编程(十):收集器的使用技巧和注意事项,需要的朋友参考一下 前面我们已经用过几次collect()方法来将Stream返回的元素拼成ArrayList了。这是一个reduce操作,它对于将一个集合转化成另一种类型(通常是一个可变的集合)非常有用。collect()函数,如果和Collectors工具类里的一些方法结合起来使用的话,

  • 问题内容: 如何在Java中模拟函数式编程,特别是如何将函数映射到项目集合? 什么是最冗长和尴尬的方法? 问题答案: 在Java之前,所有的函数式编程尝试在Java中都会有些 冗长 和/或 笨拙 ,直到Java 8。 最 直接的 方法是提供一个接口(例如Guava的这种形式),并提供采用和调用该接口的各种方法(例如我认为您的方法应该执行的操作)。 不好的事情是,您需要使用匿名内部类来实现并经常这样

  • 执行从右到左的函数组合。 使用 Array.reduce() 来执行从右到左的函数组合。最后(最右边的)函数可以接受一个或多个参数;其余的功能必须是一元的。 const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args))); const add5 = x => x + 5; const multiply =