I would like to find a way to take the object specific routine below and abstract it into a method that you can pass a class, list, and fieldname to get back a Map. If I could get a general pointer on the pattern used or , etc that could get me started in the right direction.
Map<String,Role> mapped_roles = new HashMap<String,Role>();
List<Role> p_roles = (List<Role>) c.list();
for (Role el : p_roles) {
mapped_roles.put(el.getName(), el);
}
to this? (Pseudo code)
Map<String,?> MapMe(Class clz, Collection list, String methodName)
Map<String,?> map = new HashMap<String,?>();
for (clz el : list) {
map.put(el.methodName(), el);
}
is it possible?
Java 8 streams and method references make this so easy you don't need a helper method for it.
If there may be duplicate keys, you can aggregate the values with the toMap overload that takes a value merge function, or you can use groupingBy to collect into a list:
As shown above, none of this is specific to String -- you can create an index on any type.
If you have a lot of objects to process and/or your indexing function is expensive, you can go parallel by using
Collection.parallelStream()
orstream().parallel()
(they do the same thing). In that case you might use toConcurrentMap or groupingByConcurrent, as they allow the stream implementation to just blast elements into a ConcurrentMap instead of making separate maps for each thread and then merging them.If you don't want to commit to
Foo::getName
(or any specific method) at the call site, you can use a Function passed in by a caller, stored in a field, etc.. Whoever actually creates the Function can still take advantage of method reference or lambda syntax.Using Guava (formerly Google Collections):
Or, if you want to supply your own method that makes a
String
out of the object:Avoid reflection like the plague.
Unfortunately, Java's syntax for this is verbose. (A recent JDK7 proposal would make it much more consise.)
Currently call as:
In JDK7, it may be something like:
(Might need a
yield
in there.)If you're sure that each object in the
List
will have a unique index, use Guava with Jorn's suggestion ofMaps.uniqueIndex
.If, on the other hand, more than one object may have the same value for the index field (which, while not true for your specific example perhaps, is true in many use cases for this sort of thing), the more general way do this indexing is to use Multimaps.index(Iterable<V> values, Function<? super V,K> keyFunction) to create an
ImmutableListMultimap<K,V>
that maps each key to one or more matching values.Here's an example that uses a custom
Function
that creates an index on a specific property of an object:Here's what I would do. I am not entirely sure if I am handling generics right, but oh well:
Just pass a Collection to it, and have your classes implement toString() to return the name. Polymorphism will take care of it.
Using reflection and generics:
In your documentation, make sure you mention that the return type of the method must be a String. Otherwise, it will throw a ClassCastException when it tries to cast the return value.