Подтвердить что ты не робот

Фильтрация списка JavaBeans с помощью Google Guava

В программе Java у меня есть список beans, который я хочу фильтровать на основе определенного свойства.

Например, скажем, у меня есть список Person, JavaBean, где Person имеет много свойств, среди которых "имя".

У меня также есть список имен.

Теперь я хочу найти всех лиц, имя которых указано в списке имен.

Каков наилучший способ выполнить этот фильтр с помощью Google Guava?

До сих пор я думал о объединении Guava с Apache beanutils, но это не кажется элегантным.

Я также нашел библиотеку расширений отражения здесь: http://code.google.com/p/guava-reflection/, но я не уверен, как ее использовать (там небольшая документация).

Любые мысли?

p.s. Можете ли вы сказать, что я очень скучаю по пониманию списка Python?

4b9b3361

Ответ 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);
}