Есть ли элегантный способ удалить нули при преобразовании коллекции с помощью Guava? - PullRequest
59 голосов
/ 26 ноября 2009

У меня есть вопрос об упрощении кода обработки Коллекции при использовании Google Collections ( обновление : Гуава ).

У меня есть куча "компьютерных" объектов, и я хочу получить коллекцию их "идентификаторов ресурсов". Это делается так:

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() может возвращать ноль (и изменение, которое сейчас не является опцией), но в этом случае я бы хотел опустить нули из результирующей коллекции 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-код (без всяких причудливых вещей Predicate или Function), возможно, будет намного чище:

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

Использование вышеперечисленного, безусловно, также вариант, но из любопытства (и желания узнать больше о Коллекциях Google), можете ли вы сделать ту же самую вещь более коротким или более элегантным способом, используя Google Collections

Ответы [ 5 ]

79 голосов
/ 26 ноября 2009

Уже есть предикат в 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 = ...;
}

, который очищает это еще больше (и будет способствовать повторному использованию).

35 голосов
/ 22 января 2014

Немного более симпатичный синтаксис с 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() для заливки элементов в произвольную коллекцию.

14 голосов
/ 07 октября 2014

Это заняло больше времени, чем ожидалось @ Джон Скит , но потоки Java 8 делают это простым:

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

Вы также можете использовать .filter(x -> x != null), если хотите; разница очень мала .

5 голосов
/ 26 ноября 2009

Во-первых, я бы где-нибудь создал постоянный фильтр:

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 с замыканиями и методами расширения:)

1 голос
/ 25 июня 2010

Вы можете написать свой собственный метод следующим образом. это отфильтрует нули для любой функции, которая возвращает нуль из метода apply.

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