traduccion spanish fruta examples español benefits java collections guava

java - spanish - guava maven



¿Es Google Guava "más difícil" de usar que las colecciones Apache? (2)

Como uno de los desarrolladores de Guava, obviamente estoy parcializado, pero aquí hay algunos comentarios.

La facilidad de uso fue uno de los objetivos principales detrás del diseño de Guava. Siempre hay espacio para mejorar, y estamos ansiosos por escuchar cualquier sugerencia o inquietud. Por lo general, hay una razón detrás de las decisiones de diseño, aunque probablemente todos puedan encontrar cosas con las que personalmente no están de acuerdo.

En términos de las vistas en vivo, ColinD describió las ventajas de rendimiento que existen para algunos casos de uso. Además, a veces desea que los cambios en la vista alteren la colección original y viceversa.

Ahora, hay casos en los que copiar la colección proporciona un mejor rendimiento, pero solo se necesita una línea de código para hacerlo. Si bien Guava podría incluir métodos transformAndCopy (), omitimos métodos de una línea, excepto en casos extremadamente comunes como Maps.newHashMap (). Cuantos más métodos estén presentes, más difícil será encontrar el método que necesita.

Estoy pensando en pedirle a mi equipo, de niveles de habilidad mixtos, que use Google Guava. Antes de Guayaba, habría usado las Colecciones Apache (o su versión generada).

La guayaba, a diferencia de las colecciones de Apache, parece ser más fuerte en algunos aspectos, pero tal vez sea menos fácil de usar para los programadores menos experimentados. Aquí hay un área donde creo que podría ejemplificar eso.

El código que he heredado contiene una gran cantidad de bucles en listas de lo que son esencialmente mapas de valores heterogéneos, sondeando valores, realizando controles nulos y luego haciendo algo trivial:

boolean foo( final List< MapLike > stuff, final String target ) { final String upperCaseTarget = target.toUpperCase(0; for( MapLike m : stuff ) { final Maplike n = (MapLike) m.get( "hard coded string" ); if( n != null ) { final String s = n.get( "another hard code string" ); if( s != null && s.toUpperCase().equals( upperCaseTarget ) ) { return true ; } } return false ; }

Mi pensamiento inicial fue usar Transformers de Apache Collections:

boolean foo( final List< MapLike > stuff, final String target ) { Collection< String> sa = (Collection< String >) CollectionUtils.collect( stuff, TransformerUtils.chainedTransformer( new Transformer[] { AppUtils.propertyTransformer("hard coded string"), AppUtils.propertyTransformer("another hard coded string"), AppUtils.upperCaseTransformer() } ) ); return sa.contains( target.toUpperCase() ) ; }

Usando guayaba, puedo ir de dos maneras:

boolean foo( final List< MapLike > stuff, final String target ) { Collection< String > sa = Collections2.transform( stuff, Functions.compose( AppUtils.upperCaseFunction(), Functions.compose( AppUtils.propertyFunction("another hard coded string"), AppUtils.propertyFunction("hard coded string") ) ) ); return sa.contains( target.toUpperCase() ) ; // or // Iterables.contains( sa, target.toUpperCase() ); // which actually doesn''t buy me much

}

En comparación con las colecciones de Apache, Functions.compose (g, f) invierte el orden "intuitivo": las funciones se aplican de derecha a izquierda, en lugar del "obvio" de izquierda a derecha de TransformerUtils.chainedTransformer.

Un problema más sutil es que, cuando Guava devuelve una vista en vivo, es probable que las llamadas contains en la vista en vivo apliquen la función (compuesta) varias veces, por lo que lo que realmente debo hacer es:

return ImmutableSet.copy( sa ).contains( target.toUpperCase() ) ;

Pero podría tener nulos en mi conjunto transformado, así que no puedo hacer eso. Puedo volcarlo en un java.util.Collection, por supuesto.

Pero eso no va a ser obvio para mi equipo (menos experimentado), y es probable que se pierda en el calor de la codificación incluso después de que lo explique. Esperaba que tal vez Iterables.contains () "hiciera lo correcto" y conociera algún ejemplo de magia para distinguir un proxy de vista en vivo de una antigua colección simple, pero no lo hace. Eso hace que la guayaba sea más difícil de usar.

¿Quizás escribo algo como un método estático en mi clase de utilidad para manejar esto?

// List always uses linear search? So no value in copying? // or perhaps I should copy it into a set? boolean contains( final List list, final Object target ) { return list.contains( target ) ; } // Set doesn''t use linear search, so copy? boolean contains( final Set set, final Object target ) { //return ImmutableSet.copy( set ).contains( target ) ; // whoops, I might have nulls return Sets.newHashSet( set ).contains( target ) ; }

¿O quizás solo copiar conjuntos por encima de cierto tamaño?

// Set doesn''t use linear search, so copy? boolean contains( final Set set, final Object target ) { final Set search = set.size() > 16 : Sets.newHashSet( set ) : set ; return search.contains( target ) ; }

Supongo que estoy preguntando "por qué no hay una transform ''más fácil'' en Guava", y supongo que la respuesta es: "bien, simplemente descargue lo que devuelve en una nueva colección, o escriba su propia transformación que sí lo haga". ese".

Pero si necesito hacer eso, ¿no podrían otros clientes de las bibliotecas de Guayaba? Tal vez hay una mejor manera de hacerlo en Guava, que no conozco?


Yo diría que la Guayaba definitivamente no es más difícil de usar que las Colecciones Apache. Yo diría que es mucho más fácil, en realidad.

Uno de los puntos importantes en la ventaja de Guava es que no expone tantos tipos de objetos nuevos ... le gusta mantener ocultos la mayoría de los tipos de implementación reales que utiliza detrás de los métodos de fábrica estática que solo exponen la interfaz. Tomemos los distintos Predicate , por ejemplo. En Apache Collections, tiene clases de implementación pública de nivel superior como:

NullPredicate NotNullPredicate NotPredicate AllPredicate AndPredicate AnyPredicate OrPredicate

Más una tonelada más.

En Guayaba, estos se empaquetan cuidadosamente en una sola clase de nivel superior, Predicates :

Predicates.isNull() Predicates.notNull() Predicates.not(...) Predicates.and(...) Predicates.or(...)

Ninguno de ellos expone su clase de implementación, ¡porque no necesitas saberlo! Si bien las colecciones Apache tienen un PredicateUtils equivalente, el hecho de que exponga los tipos de su Predicate s hace que sea más difícil de usar. Como lo veo, las colecciones de Apache son solo un montón de clases visibles innecesarias y partes no muy útiles que agregan desorden y dificultan el acceso y uso de las partes útiles. La diferencia es clara cuando se observa el número de clases e interfaces que exponen las dos bibliotecas:

  • Apache Collections expone 309 tipos.
  • La guayaba, incluidos todos sus paquetes (no solo las colecciones), expone solo 191 tipos.

Agregue a eso la forma en que Guava es mucho más cuidadoso solo para incluir utilidades y clases realmente útiles, su rigurosa adhesión a los contratos de las interfaces que implementa, etc. y creo que es una biblioteca de mayor calidad y más fácil de usar.

Para abordar algunos de sus puntos específicos:

De hecho, creo que el orden que eligió Guava para Functions.compose es más intuitivo (aunque creo que para empezar es un argumento bastante subjetivo). Tenga en cuenta que en su ejemplo de composición con guayaba, el orden en que se aplicarán las funciones se lee desde el final de la declaración hacia el lugar donde se asigna el resultado final. Otro problema con su ejemplo es que, para comenzar, no es seguro para el tipo, ya que el ejemplo original implica convertir el resultado del método get a otro tipo. Una ventaja de la compose de Guava sobre la matriz de Transformer s en el ejemplo de Apache Commons es que compose puede hacer una composición de funciones de tipo seguro, asegurando (en tiempo de compilación) que la serie de funciones que está aplicando funcionará correctamente. La versión de Apache es completamente insegura en este sentido.

Las vistas son superiores a las copias:

En segundo lugar, sobre el "problema" de visualización en vivo de Collections2.transform . Para ser franco, estás completamente equivocado en ese punto. ¡El uso de una vista en vivo en lugar de copiar todos los elementos de la Collection original en una nueva Collection es en realidad mucho más eficiente! Esto es lo que sucederá cuando llame a Collections2.transform y luego la llamada contains en la Collection que devuelve:

  • Se crea una vista Collection envuelve el original ... el original y la Function se asignan simplemente a los campos que contiene.
  • Se recupera el iterador de la Collection .
  • Para cada elemento en el Iterator , se aplicará la Function , obteniendo el valor transformado de ese elemento.
  • Cuando se encuentra el primer elemento para el cual el valor transformado equals al objeto que está verificando, contains retornará. ¡Solo se itera (y se aplica la Function ) hasta que se encuentra una coincidencia! La Function se aplica como máximo una vez por elemento!

Esto es lo que hace la versión de Apache Collections:

  • Crea una nueva ArrayList para almacenar los valores transformados.
  • Obtiene el iterador de la Collection original.
  • Para cada elemento en el iterador de la Collection original, aplica la función y agrega el resultado a la nueva Collection . Esto se hace para cada elemento de la Collection original, incluso si el resultado de aplicar el Transformer al primer elemento hubiera coincidido con el objeto que estamos buscando.
  • Luego , contains iterará sobre cada elemento en la nueva Collection buscando el resultado.

Estos son los mejores y peores escenarios para una Collection de tamaño N que utiliza ambas bibliotecas. El mejor caso es cuando el valor transformado del primer elemento equals al objeto que está buscando con contains y el peor caso es cuando el valor que está buscando con contains no existe en la colección transformada.

  • Guayaba
    • Mejor caso : itera 1 elemento, aplica el tiempo de la Function 1, almacena 0 elementos adicionales.
    • El peor de los casos : itera N elementos, aplica la Function N veces, almacena 0 elementos adicionales.
  • Apache
    • Mejor caso : itera N + 1 elementos, aplica Transformer N veces, almacena N elementos adicionales (la colección transformada).
    • El peor de los casos : itera elementos 2N, aplica Transformer N veces, almacena N elementos adicionales (la colección transformada).

Espero que sea obvio por lo anterior que, en general, ¡una vista es algo muy bueno! Además, es realmente fácil copiar una vista en una colección que no sea vista en cualquier momento que sea útil, y que tendrá el mismo rendimiento que la versión de Apache para empezar. Sin embargo, decididamente no sería útil en ninguno de los ejemplos que ha dado.

Como una pequeña nota final, Iterables.contains existe simplemente para permitirte verificar si un Iterable que no sabes que es una Collection contiene un valor. Si el Iterable le das es en realidad una Collection , simplemente llama contains() en esa Collection para permitir un posible mejor rendimiento (si es un Set , por ejemplo).