有没有一个优雅的方式来消除使用番石榴转换collections时的空值?

使用Google Collections( 更新 : Guava )时,我有一个关于简化一些Collection处理代码的问题。

我有一堆“计算机”对象,我想结束他们的“资源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代码(根本没有花哨的谓词或者function)可以说是更清洁的:

 Collection<String> resourceIds = Lists.newArrayList(); for (Computer computer : matchingComputers) { String resourceId = computer.getResourceId(); if (resourceId != null) { resourceIds.add(resourceId); } } 

使用上述方法当然也是一种select,但出于好奇(希望了解更多Google Collections), 您是否可以使用Google Collections以更短或更优雅的方式完成同样的事情

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 = ...; } 

这更清理了这个(并将促进重用)。

FluentIterable (自番石榴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()方法将元素倒入任意集合中。

花费的时间比@Jon Skeet预期的要长 ,但是Java 8stream确实使这一点变得简单:

 List<String> resourceIds = computers.stream() .map(Computer::getResourceId) .filter(Objects::nonNull) .collect(Collectors.toList()); 

如果你喜欢,你也可以使用.filter(x -> x != null) 。 差别很小 。

首先,我会在某处创build一个常量filter:

 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)); 

您可以在代码中的任何地方使用相同的空filter。

如果你在其他地方使用同样的计算function,你也可以使它保持不变,只留下:

 Collection<String> resourceIds = Lists.newArrayList( Iterables.filter( Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION), NULL_FILTER)); 

这当然不如C#等价物那么好,但是在Java 7中,这样做会更好用闭包和扩展方法:)

你可以像这样写自己的方法。 这将过滤出任何从apply方法返回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);