Filtrowanie listy JavaBeans za pomocą Google Guava

W programie Java mam listę ziaren, które chcę filtrować na podstawie określonej właściwości.

Na przykład, powiedzmy, że mam listę osób, JavaBean, gdzie osoba ma wiele właściwości, wśród nich 'nazwa'.

Mam też listę nazwisk.

Teraz chcę znaleźć wszystkie osoby, których nazwisko jest na liście imion.

Jaki jest najlepszy sposób na wykonanie tego filtra za pomocą Google Guava?

Do tej pory myślałem o połączeniu Guava z Apache beanutils, ale to nie wygląda elegancko.

Znalazłem również bibliotekę reflection extension tutaj: http://code.google.com/p/guava-reflection/ , ale nie jestem pewien, jak z niego korzystać (jest trochę dokumentacji).

Jakieś pomysły?

P. S. czy możesz powiedzieć, że naprawdę tęsknię za listą Pythona?

Author: Or Peles, 2011-12-26

9 answers

Zrób to w staromodny sposób, bez guawy. (Mówiąc Jako deweloper Guava.)

List<Person> filtered = Lists.newArrayList();
for(Person p : allPersons) {
   if(acceptedNames.contains(p.getName())) {
       filtered.add(p);
   }
}

Można to zrobić z Guava, ale Java nie jest Pythonem, a próba przekształcenia go w Pythona po prostu utrwali niewygodny i nieczytelny kod. Narzędzia funkcjonalne Guava powinny być używane oszczędnie i tylko wtedy, gdy zapewniają konkretne i wymierne korzyści dla obu linii kodu lub wydajności.

 42
Author: Louis Wasserman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-24 16:13:49
Iterable<Person> filtered = Iterables.filter(allPersons, new Predicate<Person>() {
    @Override
    public boolean apply(Person p) {
        return acceptedNames.contains(p.getName());
    }
});

Jeśli Twoja lista nazw jest duża, lepiej przekształć ją w zestaw (HashSet, preferowane) i wywołaj contains na tym zestawie, a nie listę, ponieważ contains to O(1) dla HashSet, A O(n) dla listy.

 23
Author: JB Nizet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-26 14:26:52

Wyjaśnienie wątpliwości ze zdania:

Do tej pory myślałem o połączeniu Guava z Apache beanutils, ale to nie wygląda elegancko.

Java, mimo że jest tak popularna, nie obsługuje funkcji pierwszej klasy *, Co to jest podlega zmianom w Javie 8 , gdzie będzie można zrobić:

Iterable <Person> filtered = filter(allPersons, (Person p) -> acceptedNames.contains(p.getName()));
Z lambda i będzie elegancko.

Do tego czasu masz do wyboru:

  • old-school way (jak napisał @ Louis)
  • verbose Guava filter (@JB ' s answer)
  • lub innych funkcjonalnych bibliotek Javy (odpowiedź@superfav).

Chciałbym też dodać do odpowiedzi @ Lois, że Guava-sposobem na stworzenie niezmiennego zbioru , ponieważ są lepsze niż niezmodyfikowalne , co jest również opisane w poz. 15, Minimalizuj zmienność W efektywnej Javie autorstwa Joshuy Blocha**:

ImmutableList.Builder<Person> builder = ImmutableList.builder();
for (final Person p : allPersons) {
    if (acceptedNames.contains(p.getName())) {
        builder.add(p);
    }
}
ImmutableList<Person> filtered = builder.build();

(to szczegóły implementacji, które ImmutableList.Builder tworzy tymczasowe ArrayList pod maską).

*: Bardzo mi to przeszkadza, pochodzę ze światów Python, JavaScript i Perl, gdzie funkcje są traktowane lepiej

**: guawa i Bloch są ściśle powiązane na wiele sposobów ;)

 5
Author: Xaerxess,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-27 09:40:38

Nie mogę się wystarczająco zgodzić z odpowiedziami Louisa i JB. Nie wiedziałem, że guava-reflection, może LambdaJ może być tym, czego szukasz:

// 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());

A może Scala, Clojure lub Groovy są tym, czego szukasz...

 4
Author: superfav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-27 02:30:11

Mówiąc Jako deweloper guava-reflection, przepraszam, że porzuciłem ten projekt na tak wczesnym etapie (mam pracę dzienną i żonę i dzieci :-)). Moja wizja była jak:

Iterable<Object> thingsWithNames = 
    Iterables.filter(someData,
                     // this is a Predicate, obviously
                     BeanProperties.hasBeanProperty("name", String.class));

Istniejący kod jest tam około 60%, więc jeśli jesteś zainteresowany, skontaktuj się ze mną, a może uda nam się to zakończyć razem.

 2
Author: Sean Patrick Floyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-27 13:21:20

Jeśli używasz LinkedList (lub jakiegokolwiek innego zbioru, który usuwa nie jest zbyt pracochłonny) w aplikacji jednowątkowej najbardziej skutecznym rozwiązaniem jest:

final Iterator<User> userIterator = users.iterator();
while (userIterator.hasNext()) {
    if (/* your condition for exclusion */) {
        userIterator.remove();
    }
}
 0
Author: Pavel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-20 14:28:39

W Java8 style możesz użyć stream + filter, aby osiągnąć swój cel.

persons.stream()
            .filter(p -> names.contains(p.getName()))
            .collect(Collectors.toList());
 0
Author: enterbios,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-06 12:57:14

W Java8 możesz użyć Collection.removeIf ()

List<Person> theList = ...;
theList.removeIf(
    (Person p)->"paul".equals(p.getName())
);

To oczywiście zmieni aktualną listę.

 0
Author: user2051552,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-29 23:48:54

Oto przykład użycia generyków za pomocą guava, beanutils do filtrowania dowolnej listy za pomocą żądanego dopasowania

/**
 * 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);
}
 0
Author: Anand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-24 07:06:29