59

在使用 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 集合中省略 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 Collections 的愿望),您可以使用 Google Collections 以更短或更优雅的方式做同样的事情吗?

4

5 回答 5

80

这里已经有一个谓词Predicates可以帮助您 -- Predicates.notNull()-- 并且您可以使用Iterables.filter()和事实,Lists.newArrayList()可以用 anIterable来进一步清理它。

Collection<String> resourceIds = Lists.newArrayList(
  Iterables.filter(
     Iterables.transform(matchingComputers, yourFunction),
     Predicates.notNull()
  )
);

如果您实际上不需要 a Collection,只需要 a ,Iterable那么Lists.newArrayList()呼叫也可以消失,您再次清洁了一步!

我怀疑您可能会发现Functionwill 再次派上用场,并且最有用的声明为

public class Computer {
    // ...
    public static Function<Computer, String> TO_ID = ...;
}

这会更加清理它(并将促进重用)。

于 2009-11-26T10:43:16.700 回答
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()方法将元素倒入任意集合中。

于 2014-01-22T15:06:02.540 回答
14

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

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

如果您愿意,也可以使用.filter(x -> x != null)差别很小

于 2014-10-07T03:00:50.560 回答
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 中这一切都会变得更好,带有闭包和扩展方法:)

于 2009-11-26T09:52:31.820 回答
1

您可以像这样编写自己的方法。这将为从 apply 方法返回 null 的任何 Function 过滤掉 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);
于 2010-06-25T05:08:29.877 回答