有没有一种优雅的方法可以在使用 Guava 转换集合时删除空值?
Posted
技术标签:
【中文标题】有没有一种优雅的方法可以在使用 Guava 转换集合时删除空值?【英文标题】:Is there an elegant way to remove nulls while transforming a Collection using Guava? 【发布时间】:2010-12-20 15:10:16 【问题描述】:我有一个关于在使用 Google Collections 时简化一些 Collection 处理代码的问题(更新:Guava)。
我有一堆“计算机”对象,我想以它们的“资源 id”集合结束。这样做是这样的:
Collection<Computer> matchingComputers = findComputers();
Collection<String> resourceIds =
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>()
public String apply(Computer from)
return from.getResourceId();
));
现在,getResourceId()
可能会返回 null(现在不能更改它),但在这种情况下,我想从生成的 String 集合中省略 null。
这是过滤空值的一种方法:
Collections2.filter(resourceIds, new Predicate<String>()
@Override
public boolean apply(String input)
return input != null;
);
你可以像这样把所有这些放在一起:
Collection<String> resourceIds = Collections2.filter(
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>()
public String apply(Computer from)
return from.getResourceId();
)), new Predicate<String>()
@Override
public boolean apply(String input)
return input != null;
);
但是对于这样一个简单的任务来说,这并不优雅,更不用说可读了!事实上,普通的旧 Java 代码(根本没有花哨的 Predicate 或 Function 的东西)可以说会更干净:
Collection<String> resourceIds = Lists.newArrayList();
for (Computer computer : matchingComputers)
String resourceId = computer.getResourceId();
if (resourceId != null)
resourceIds.add(resourceId);
使用上述方法当然也是一种选择,但出于好奇(以及想了解更多 Google 收藏集),您能否使用 Google 收藏集以更短或更优雅的方式做同样的事情?
【问题讨论】:
【参考方案1】:FluentIterable
有点“漂亮”的语法(从 Guava 12 开始):
ImmutableList<String> resourceIds = FluentIterable.from(matchingComputers)
.transform(getResourceId)
.filter(Predicates.notNull())
.toList();
static final Function<Computer, String> getResourceId =
new Function<Computer, String>()
@Override
public String apply(Computer computer)
return computer.getResourceId();
;
请注意,返回的列表是ImmutableList
。但是,您可以使用copyInto()
方法将元素倒入任意集合中。
【讨论】:
【参考方案2】:Predicates
中已经有一个谓词可以在这里为您提供帮助 -- Predicates.notNull()
-- 您可以使用 Iterables.filter()
并且事实上 Lists.newArrayList()
可以使用 Iterable
来进一步清理它.
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(
Iterables.transform(matchingComputers, yourFunction),
Predicates.notNull()
)
);
如果您实际上不需要Collection
,只需要Iterable
,那么Lists.newArrayList()
呼叫也可以消失,您又会更清洁!
我怀疑您可能会发现Function
会再次派上用场,并且最有用的是声明为
public class Computer
// ...
public static Function<Computer, String> TO_ID = ...;
这会更加干净(并且会促进重用)。
【讨论】:
很好的建议,谢谢!使用 Predicates.notNull() 并将函数放在一个常量中确实使代码更加清晰。 太棒了:)。当我使用函数作为转换时,我喜欢将它隔离在一个静态方法中并将其命名为 intoXXX(),我觉得它很容易阅读。在这种情况下,它可能是:transform(matchingCompters, intoResourceId()).【参考方案3】:它比@Jon Skeet expected 花费了更长的时间,但 Java 8 流确实让这变得简单:
List<String> resourceIds = computers.stream()
.map(Computer::getResourceId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
如果你愿意,也可以使用.filter(x -> x != null)
; the difference is very minor.
【讨论】:
我正在寻找与 guava 的Predicates#notNull()
等效的 Java8。没想到看Objects
这里是 API 文档:docs.oracle.com/javase/8/docs/api/java/util/…【参考方案4】:
您可以像这样编写自己的方法。这将为从 apply 方法返回 null 的任何函数过滤掉 null。
public static <F, T> Collection<T> transformAndFilterNulls(List<F> fromList, Function<? super F, ? extends T> function)
return Collections2.filter(Lists.transform(fromList, function), Predicates.<T>notNull());
然后可以使用以下代码调用该方法。
Collection c = transformAndFilterNulls(Lists.newArrayList("", "SD", "DDF"), new Function<String, Long>()
@Override
public Long apply(String s)
return s.isEmpty() ? 20L : null;
);
System.err.println(c);
【讨论】:
【参考方案5】:首先,我会在某处创建一个常量过滤器:
public static final Predicate<Object> NULL_FILTER = new Predicate<Object>()
@Override
public boolean apply(Object input)
return input != null;
那么你可以使用:
Iterable<String> ids = Iterables.transform(matchingComputers,
new Function<Computer, String>()
public String apply(Computer from)
return from.getResourceId();
));
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(ids, NULL_FILTER));
您可以在代码中的任何地方使用相同的空过滤器。
如果您在其他地方使用相同的计算函数,您也可以将其设为常数,只需:
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(
Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION),
NULL_FILTER));
它肯定不如 C# 等效的那么好,但是这一切都将在 Java 7 中通过闭包和扩展方法变得更好 很多 :)
【讨论】:
我个人称它为 NOT_NULL_FILTER。 :) Predicates 类中已经有一个静态方法(见我的回答)。 @Cowan:这取决于您如何对待“过滤器”——您可能会争辩说它会过滤 空值。这是命名方面的普遍痛苦。但是,我认为它会做什么很明显,因为反过来会很愚蠢:) 不过对 Predicates 方法的调用很好。以上是关于有没有一种优雅的方法可以在使用 Guava 转换集合时删除空值?的主要内容,如果未能解决你的问题,请参考以下文章
有没有一种优雅的方法可以将 BQ 嵌套字段转换为 key:value JSON?
优雅代码14-guava精选方法及eventBus观察者模式源码解析