Фильтрация списка JavaBeans с помощью Google Guava
В программе Java у меня есть список beans, который я хочу фильтровать на основе определенного свойства.
Например, скажем, у меня есть список Person, JavaBean, где Person имеет много свойств, среди которых "имя".
У меня также есть список имен.
Теперь я хочу найти всех лиц, имя которых указано в списке имен.
Каков наилучший способ выполнить этот фильтр с помощью Google Guava?
До сих пор я думал о объединении Guava с Apache beanutils, но это не кажется элегантным.
Я также нашел библиотеку расширений отражения здесь: http://code.google.com/p/guava-reflection/, но я не уверен, как ее использовать (там небольшая документация).
Любые мысли?
p.s. Можете ли вы сказать, что я очень скучаю по пониманию списка Python?
Ответы
Ответ 1
Сделайте это старомодно, без Гуавы. (Говоря как разработчик Guava.)
List<Person> filtered = Lists.newArrayList();
for(Person p : allPersons) {
if(acceptedNames.contains(p.getName())) {
filtered.add(p);
}
}
Вы можете сделать это с помощью Guava, но Java не является Python, и попытка сделать его в Python просто собирается увековечить неудобный и нечитаемый код. Функциональные утилиты Guava следует использовать экономно и только тогда, когда они обеспечивают конкретное и измеримое преимущество для обеих строк кода или производительности.
Ответ 2
Iterable<Person> filtered = Iterables.filter(allPersons, new Predicate<Person>() {
@Override
public boolean apply(Person p) {
return acceptedNames.contains(p.getName());
}
});
Если ваш список имен большой, вам лучше преобразовать его в Set (HashSet, предпочтительнее), а вызов содержит в этом наборе, а не в списке, потому что содержит O (1) для HashSet и O (n) для списка.
Ответ 3
Объяснение ваших сомнений из предложения:
До сих пор я думал о объединении Guava с Apache beanutils, но это не кажется элегантным.
Java, несмотря на то, что она так популярна, не имеет первоклассная функция поддерживает * что может быть изменено в Java 8, где вы сможете:
Iterable <Person> filtered = filter(allPersons, (Person p) -> acceptedNames.contains(p.getName()));
С лямбдами, и это будет элегантно.
До тех пор вы можете выбрать между:
- Старый путь школы (как писал @Louis)
- подробный фильтр Гуавы (ответ @JB)
- или другие функциональные библиотеки Java (ответ @superfav).
Я также хотел бы добавить к @Lois ответ, что Guava-way будет создавать неизменяемую коллекцию, потому что они лучше, чем не поддающиеся модификации, что также описано в п. 15, Минимизируйте изменчивость в Эффективной Java Джошуа Блох **:
ImmutableList.Builder<Person> builder = ImmutableList.builder();
for (final Person p : allPersons) {
if (acceptedNames.contains(p.getName())) {
builder.add(p);
}
}
ImmutableList<Person> filtered = builder.build();
(Эта деталь реализации, которая ImmutableList.Builder
создает временный ArrayList
под капотом).
*: меня это очень беспокоит, я пришел из миров Python, JavaScript и Perl, где функции обрабатываются лучше
**: Гуава и Блох тесно связаны друг с другом;)
Ответ 4
Я не могу согласиться с ответами Луи и Дж. Я не знал гуаво-рефлексии, может быть LambdaJ может быть тем, что вы ищете:
// set up
Person me = new Person("Favio");
Person luca = new Person("Luca");
Person biagio = new Person("Biagio");
Person celestino = new Person("Celestino");
Collection<Person> meAndMyFriends = asList(me, luca, biagio, celestino);
// magic
Collection<Person> filtered = filter(having(on(Person.class).getName(),
isOneOf("Favio", "Luca")),
meAndMyFriends);
// test
assertThat(filtered, hasItems(me, luca));
assertEquals(2, filtered.size());
Или, может быть, Scala, Clojure или Groovy - это то, что вы ищете...
Ответ 5
Говоря как разработчик guava-reflection, мне жаль, что я отказался от этого проекта на столь раннем этапе (у меня есть дневная работа, а жена и дети:-)). Мое видение было чем-то вроде:
Iterable<Object> thingsWithNames =
Iterables.filter(someData,
// this is a Predicate, obviously
BeanProperties.hasBeanProperty("name", String.class));
Существующий код составляет около 60%, поэтому, если вы заинтересованы, свяжитесь со мной и, возможно, мы сможем завершить это.
Ответ 6
Если вы используете LinkedList
(или любую другую коллекцию, которая удаляет отпечатки, не очень трудоемко) в однопоточном приложении наиболее эффективным решением является:
final Iterator<User> userIterator = users.iterator();
while (userIterator.hasNext()) {
if (/* your condition for exclusion */) {
userIterator.remove();
}
}
Ответ 7
С помощью стиля Java8 вы можете использовать фильтр потока + для достижения своей цели.
persons.stream()
.filter(p -> names.contains(p.getName()))
.collect(Collectors.toList());
Ответ 8
С помощью Java8 вы можете использовать Collection.removeIf()
List<Person> theList = ...;
theList.removeIf(
(Person p)->"paul".equals(p.getName())
);
Это, конечно, изменит текущий список.
Ответ 9
Вот пример использования дженериков с использованием guava, beanutils для фильтрации любого списка с помощью запрошенного соответствия
/**
* Filter List
*
* @param inputList
* @param requestMatch
* @param invokeMethod
* @return
*/
public static <T> Iterable<T> predicateFilterList(List<T> inputList, final String requestMatch,
final String invokeMethod) {
Predicate<T> filtered = new Predicate<T>() {
@Override
public boolean apply(T input) {
boolean ok = false;
try {
ok = BeanUtils.getProperty(input, invokeMethod).equalsIgnoreCase(requestMatch);
}
catch (Exception e) {
e.printStackTrace();
}
return ok;
}
};
return Iterables.filter(inputList, filtered);
}