Есть ли элегантный способ удалить нули при преобразовании коллекции с помощью 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?
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