在使用Google收藏集(更新: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集合中省略空值。
这是过滤掉空值的一种方法:
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代码(根本没有花哨的谓词或函数)可以说更清晰:
Collection<String> resourceIds = Lists.newArrayList();
for (Computer computer : matchingComputers) {
String resourceId = computer.getResourceId();
if (resourceId != null) {
resourceIds.add(resourceId);
}
}
使用上述内容当然也是一种选择,但出于好奇(并希望了解更多Google集群),可以使用Google Collections以更短或更优雅的方式完成同样的事情
答案 0 :(得分:78)
Predicates
中已有一个谓词可以帮助您 - Predicates.notNull()
- 您可以使用Iterables.filter()
以及Lists.newArrayList()
可以使用Iterable
这一事实1}}再清理一下。
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 = ...;
}
可以更清洁它(并且会促进重复使用)。
答案 1 :(得分:35)
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 :(得分:14)
花费的时间比@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
答案 3 :(得分: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中使用闭包和扩展方法获得 lot 更好:)
答案 4 :(得分:1)
您可以编写自己的方法。这将过滤掉apply方法中返回null的任何Function的空值。
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);