2011-12-26 13 views
22

En un programa Java, tengo una lista de beans que deseo filtrar en función de una propiedad específica.Filtrar una lista de JavaBeans con Google Guava

Por ejemplo, supongo que tengo una lista de Persona, un JavaBean, donde Person tiene muchas propiedades, entre ellas 'nombre'.

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 con Google Guava?

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

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

¿Alguna idea?

p.s. ¿Puedes decir que realmente extraño la comprensión de la lista de Python?

Respuesta

42

Hazlo a la vieja usanza, sin guayaba. (Hablando como desarrollador de guayaba.)

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

Usted puede hacer esto con guayaba, pero Java no es Python, y tratando de hacerlo en Python es sólo va a perpetuar el código torpe e ilegible. Las utilidades funcionales de Guava se deben usar con moderación, y solo cuando proporcionan un beneficio concreto y mensurable a las líneas de código o al rendimiento.

+0

La principal diferencia entre esta solución y la mía es que esta solución crea una nueva lista que contiene las personas filtradas, mientras que la mía crea una vista sobre la lista original. Si uno u otro es preferible depende del caso de uso. –

+0

Sí, depende del caso de uso, pero diría que en el 90% de los casos de uso, las mejoras de legibilidad superan los pequeños beneficios de rendimiento. –

+3

Pareces pensar que las dos únicas áreas de compromiso son la legibilidad y el rendimiento. La corrección es mucho más importante que la (supuesta) legibilidad y el rendimiento. Si lo hace de esta manera, no solo debe probar su lógica de predicado, sino también su lógica de filtrado. Se duplicó innecesariamente (al menos, debido al estado mutable introducido) la cantidad de cosas que pueden salir mal. Multiplica esto por la cantidad de veces que filtras en una base de código ... ¿por qué querrías trabajar de esta manera? –

22
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, es mejor que lo transforman en un conjunto (HashSet, preferentemente) y llamadas contiene en este conjunto, en lugar de la lista, ya que contiene es O (1) para una HashSet y O (n) para una lista.

+1

¿Cuál debe ser transformado en un conjunto es la lista de nombres aceptados, no es la lista de las personas. Eso es lo que contiene se llama. La transformación de la lista de personas en un conjunto no tiene ningún valor agregado. –

+0

@ El comentario de Daniel sobre la otra respuesta proporciona un contexto sobre por qué esto sería preferible en algunos casos. – studgeek

3

No puedo estar tan de acuerdo con las respuestas de Louis y JB. No sabía guayaba-reflexión, tal vez LambdaJ podría ser lo que buscas:

// 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 Scala, Clojure o maravilloso de lo que se está buscando ...

5

Al explicar sus dudas de la oración:

Hasta ahora, he pensado en combinar guayaba con beanutils Apache, pero que no parece elegante.

Java, a pesar de ser tan popular, carece de apoyo first-class function*, lo que es subject to change in 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 has de elegir entre:

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

También me gustaría añadir a @ respuesta de Lois que Guava-way would be to create immutable collection, porque they are better than unmodifiable, que también se describe en el artículo 15 , Minimizar la mutabilidad en Effective Java 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 el detalle de implementación que ImmutableList.Builder crea ArrayList temporal debajo del capó).

*: me molesta mucho, vine de mundos Python, JavaScript y Perl, where functions are treated better

**: Guayaba y Bloch son fuertemente acoplado de muchas maneras;)

2

Hablando como desarrollador de guava-reflexión, lamento haber abandonado este proyecto en una etapa tan temprana (tengo un trabajo diario y una esposa & niños :-)). Mi visión era algo así como:

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

El código existente es de un 60%, así que si usted está interesado, póngase en contacto conmigo y tal vez podemos conseguir este terminó juntos.

0

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

final Iterator<User> userIterator = users.iterator(); 
while (userIterator.hasNext()) { 
    if (/* your condition for exclusion */) { 
     userIterator.remove(); 
    } 
} 
+0

¡Ay! esto no funcionará ya que se ejecutará en acceso simultáneo a la lista –

+0

¡Gracias! He arreglado mi respuesta. –

0

Con estilo Java8 puede utilizar corriente + filtrar para lograr su objetivo.

persons.stream() 
      .filter(p -> names.contains(p.getName())) 
      .collect(Collectors.toList()); 
0

Con Java8 puede utilizar Collection.removeIf()

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

Esto, por supuesto modificar la lista actual.

0

Aquí es un ejemplo del uso de genéricos usando la guayaba, BeanUtils para filtrar cualquier lista con partido solicitado

/** 
* 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); 
} 
Cuestiones relacionadas