Java 在使用 Guava 转换集合时,是否有一种优雅的方法来删除空值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1802629/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 22:48:26  来源:igfitidea点击:

Is there an elegant way to remove nulls while transforming a Collection using Guava?

javacollectionsrefactoringguava

提问by Jonik

I have a question about simplifying some Collection handling code, when using Google Collections (update: Guava).

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

I've got a bunch of "Computer" objects, and I want to end up with a Collection of their "resource id"s. This is done like so:

我有一堆“计算机”对象,我想最终得到它们的“资源 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();
    }
}));

Now, getResourceId()may return null (and changing that is not an option right now), yet in this case I'd like to omit nulls from the resulting String collection.

现在,getResourceId()可能会返回 null(并且现在更改它不是一个选项),但在这种情况下,我想从结果 String 集合中省略 null。

Here's one way to filter nulls out:

这是过滤空值的一种方法:

Collections2.filter(resourceIds, new Predicate<String>() {
    @Override
    public boolean apply(String input) {
        return input != null;
    }
});

You could put all that together like this:

你可以像这样把所有这些放在一起:

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

But this is hardly elegant, let alone readable, for such a simple task! In fact, plain old Java code (with no fancy Predicate or Function stuff at all) would arguably be much cleaner:

但这对于这样一个简单的任务来说并不优雅,更不用说可读了!事实上,普通的旧 Java 代码(根本没有花哨的 Predicate 或 Function 东西)可以说会更干净:

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

Using the above is certainly also an option, but out of curiosity (and desire to learn more of Google Collections), can you do the exact same thing in some shorter or more elegant way using Google Collections?

使用上述当然也是一种选择,但出于好奇(并希望了解更多 Google Collections),您可以使用 Google Collections 以更短或更优雅的方式做完全相同的事情吗?

采纳答案by Cowan

There's already a predicate in Predicatesthat will help you here -- Predicates.notNull()-- and you can use Iterables.filter()and the fact that Lists.newArrayList()can take an Iterableto clean this up a little more.

目前已经是一个谓语Predicates,这将帮助你在这里- Predicates.notNull()-你可以使用Iterables.filter(),而且实际上Lists.newArrayList()可以采取Iterable清理它多一点。

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

If you don't actually need a Collection, just an Iterable, then the Lists.newArrayList()call can go away too and you're one step cleaner again!

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

I suspect you might find that the Functionwill come in handy again, and will be most useful declared as

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

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

which cleans this up even more (and will promote reuse).

这会进一步清理(并会促进重用)。

回答by Jon Skeet

Firstly, I'd create a constant filter somewhere:

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

public static final Predicate<Object> NULL_FILTER =  new Predicate<Object>() {
    @Override
    public boolean apply(Object input) {
            return input != null;
    }
}

Then you can use:

然后你可以使用:

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

You can use the same null filter everywhere in your code.

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

If you use the same computing function elsewhere, you can make that a constant too, leaving just:

如果您在其他地方使用相同的计算功能,您也可以将其设为常量,只留下:

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

It's certainly not as nice as the C# equivalent would be, but this is all going to get a lotnicer in Java 7 with closures and extension methods :)

这当然不是像你一样的C#相当于会,但是这一切都将得到很多更好的在Java 7中具有封闭和扩展方法:)

回答by BewdyM8

You could write your own method like so. this will filter out nulls for any Function that returns null from the apply method.

您可以像这样编写自己的方法。这将为任何从 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());
    }

The method can then be called with the following code.

然后可以使用以下代码调用该方法。

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

回答by Natix

A bit "prettier" syntax with FluentIterable(since Guava 12):

有点“漂亮”的语法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();
        }
    };

Note that the returned list is an ImmutableList. However, you can use copyInto()method to pour the elements into an arbitrary collection.

请注意,返回的列表是一个ImmutableList. 但是,您可以使用copyInto()方法将元素倒入任意集合中。

回答by Jeffrey Bosboom

It took longer than @Jon Skeet expected, but Java 8 streams do make this simple:

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

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

You can also use .filter(x -> x != null)if you like; the difference is very minor.

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