Есть ли элегантный способ удалить нули при преобразовании коллекции с помощью Guava?
-
05-07-2019 - |
Вопрос
У меня есть вопрос об упрощении кода обработки Коллекции при использовании Коллекций 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 ()
может возвращать ноль (и изменение, которое сейчас не является опцией), но в этом случае я бы хотел исключить нули из результирующей коллекции 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 ?
Решение
Уже есть предикат в Предикаты
, которые вам здесь помогут - 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
(начиная с 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 ()
для заливки элементов в произвольную коллекцию.
Это заняло больше времени, чем Ожидается @Jon Skeet , но потоки 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 # -эквивалент, но все это получит более приятный lot в Java 7 с замыканиями и методами расширения:)
Вы можете написать свой собственный метод следующим образом. это отфильтрует нули для любой функции, которая возвращает нуль из метода 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);