在使用Google集合时,我有一个关于简化某些集合处理代码的问题(更新番石榴)。

我有一堆“电脑”对象,我想最终得到他们的“资源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以更短或更优雅的方式完成同样的事情

有帮助吗?

解决方案

谓词 可以帮助你 - 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 (自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()方法将元素填充到任意集合中。

花费的时间比 @Jon Skeet预期的要长,但Java 8流确实很简单:

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

如果您愿意,也可以使用 .filter(x - &gt; x!= null); 差异非常小

首先,我会在某处创建一个常量过滤器:

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 更好:)

您可以编写自己的方法。这将过滤掉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);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top