I'm thinking of asking my team, of mixed skill levels, to use Google Guava. Prior to Guava, I'd have used the Apache Collections (or its generified version).
Guava, as opposed to Apache Collections, seems to be stronger in some ways, but perhaps less easy to use for less experienced programmers. Here's one area where I think might exemplify that.
The code I've inherited contains a lot of looping over lists of what are essentially maps of heterogeneous values, probing them for values, doing null checks, and then doing something 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 ;
}
My initial thought was to use Apache Collections Transformers:
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() ) ;
}
Using Guava, I might go two ways:
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
}
Compared to Apache Collections, Functions.compose( g, f ) reverses the "intuitive" order: functions are applied right-to-left, rather than the "obvious" left-to-right of TransformerUtils.chainedTransformer.
A more subtle issue is that, as Guava returns a live view, calling contains
on the live view is likely to apply the (composed) function multiple times, so what I really ought to do is:
return ImmutableSet.copy( sa ).contains( target.toUpperCase() ) ;
But I might have nulls in my transformed set, so I can't quite do that. I can dump it into a java.util.Collection, of course.
But that's not going to be obvious to my (less experienced) team, and will likely be missed in the heat of coding even after I explain it. I'd hoped that perhaps Iterables.contains() would "do the right thing" and know some instance-of magic to distinguish a live-view proxy from a plain old Collection, but it doesn't. That makes Guava perhaps harder to use.
Perhaps I write something like a static method in my utility class to handle this?
// 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 ) ;
}
or perhaps only copy sets above a certain size?
// 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 ) ;
}
I suppose I'm asking, "why isn't there an 'easier' transform
in Guava", and I suppose the answer is, "fine, just always dump what it returns into a new Collection, or write your own transform that does that".
But if I need to do that, might not other clients of the Guava libraries? Perhaps there's a better way that is in Guava, that I don't know about?