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



у меня есть вопрос об упрощении кода обработки некоторых коллекций при использовании коллекций Google (обновление:гуавы).



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



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



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


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

569   5  

5 ответов:

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

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

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

вы также можете использовать .filter(x -> 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 с закрытиями и методами расширения :)

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

Comments

    Ничего не найдено.