Filtrar una lista de JavaBeans con Google Guava


En un programa Java, tengo una lista de frijoles que quiero filtrar en función de una propiedad específica.

Por ejemplo, digamos que tengo una lista de Person, un JavaBean, donde Person tiene muchas propiedades, entre ellas 'name'.

También tengo una lista de nombres.

Ahora quiero encontrar todas las personas cuyo nombre está en la lista de nombres.

¿Cuál es la mejor manera de ejecutar este filtro usando Google Guava?

Hasta ahora, he pensado en combinar Guayaba con Apache beanutils, pero eso no parece elegante.

También he encontrado una biblioteca de extensión de reflexión aquí: http://code.google.com/p/guava-reflection / , pero no estoy seguro de cómo usarlo (hay poca documentación).

¿Algún pensamiento?

P. S. ¿Puedes decir que realmente echo de menos la comprensión de la lista de Python?

Author: Or Peles, 2011-12-26

9 answers

Hazlo a la antigua, sin Guayaba. (Hablando como desarrollador de guayaba.)

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

Puedes hacer esto con Guava, pero Java no es Python, y tratar de convertirlo en Python solo va a perpetuar código torpe e ilegible. Las utilidades funcionales de Guava deben usarse con moderación, y solo cuando proporcionan un beneficio concreto y medible para las líneas de código o el rendimiento.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
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());
    }
});

Si su lista de nombres es grande, será mejor que la transforme en un Conjunto (HashSet, preferrably) y llame a contains en este conjunto, en lugar de la lista, porque contains es O(1) para un HashSet, y O(n) para una Lista.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-26 14:26:52

Explicando sus dudas de la oración:

Hasta ahora, he pensado en combinar Guayaba con Apache beanutils, pero eso no parece elegante.

Java, a pesar de ser tan popular, carece de soporte para funciones de primera clase *, lo que es sujeto a cambios en Java 8 , donde usted será capaz de hacer:

Iterable <Person> filtered = filter(allPersons, (Person p) -> acceptedNames.contains(p.getName()));

Con lambdas y será elegante.

Hasta entonces tienes que elegir entre:

  • la manera de la vieja escuela (como @Louis escribió)
  • filtro de guayaba detallado (respuesta de@JB)
  • u otras bibliotecas Java funcionales (respuesta de @ superfav).

También me gustaría añadir a la respuesta de @Lois que Guava-way sería crear colección inmutable , porque son mejores que inmodificables , que también se describe en Punto 15, Minimizar la mutabilidad en Java Eficaz por Joshua Bloch**:

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

(Es detalle de implementación que ImmutableList.Builder crea ArrayList temporal bajo el capó).

*: me molesta mucho, vengo de los mundos de Python, JavaScript y Perl, donde las funciones se tratan mejor

**: La guayaba y el Bloch están estrechamente unidos de muchas maneras ;)

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-27 09:40:38

No puedo estar lo suficientemente de acuerdo con las respuestas de Louis y JB. Yo no sabía guayaba-reflexión, tal vez LambdaJ podría ser lo que usted está buscando:

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

O tal vez Scala, Clojure o Groovy son lo que estás buscando...

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-27 02:30:11

Hablando como desarrollador de guava-reflection, lamento haber abandonado este proyecto en una etapa tan temprana (tengo un trabajo diurno y una esposa e hijos :-)). Mi visión fue algo como:

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

El código existente está alrededor del 60% allí, así que si estás interesado, contáctame y tal vez podamos terminar esto juntos.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-27 13:21:20

Si se utiliza un LinkedList (o cualquier otra colección que eliminar oprations no es muy laborioso) en la aplicación de un solo hilo la solución más eficaz es:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-20 14:28:39

Con el estilo Java8 puedes usar stream + filter para lograr tu objetivo.

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-06 12:57:14

Con Java8 puedes usar la Colección.removeIf ()

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

Esto por supuesto modificará la lista actual.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-29 23:48:54

Aquí hay un ejemplo de uso de genéricos usando guayaba, beanutils para filtrar cualquier lista usando coincidencia solicitada

/**
 * 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-24 07:06:29