Prev: Systems Programmer/Support - Application Support Specialist - in Chicago, IL.- 12 months
Next: Free IMS Java and IMS DB Integration Workshops 2010 June 14th thru 25th Ehingen, München, Köln, Frankfurt
From: Robert Klemme on 11 May 2010 02:13 On 11.05.2010 07:52, Robert Klemme wrote: > On 11.05.2010 00:20, Roedy Green wrote: >> Can anyone think of a way to write a method that takes an array of X, >> and produces a HashMap<Key,X> >> >> How would you specify the name of the key field/method? >> >> Maybe you could do it by making X implement an interface that defines >> the key. >> >> Perhaps you could do it with reflection. > > I'd rather provide an interface that is responsible for the conversion > from X to Key - this is more modular. Here's the more generics savvy solution: package util; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class MapUtil { public interface Transformer<A, B> { B transform(A a); } public static <K, V> Map<K, V> createHash(V[] i, Transformer<? super V, ? extends K> t) { return createHash(Arrays.asList(i), t); } public static <K, V> Map<K, V> createHash(Iterable<? extends V> i, Transformer<? super V, ? extends K> t) { final Map<K, V> map = new HashMap<K, V>(); fill(map, i, t); return map; } public static <K, V> void fill(Map<K, V> m, Iterable<? extends V> i, Transformer<? super V, ? extends K> t) { for (V val : i) { m.put(t.transform(val), val); } } } Example usage public static void main(String[] args) { System.out.println(Arrays.asList(args)); Map<Integer, String> m = createHash(args, new Transformer<String, Integer>() { @Override public Integer transform(String a) { return a == null ? 0 : a.length(); } }); System.out.println(m); } Cheers robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/
From: Robert Klemme on 11 May 2010 05:22 On May 11, 8:08 am, Kevin McMurtrie <mcmurt...(a)pixelmemory.us> wrote: > In article <qf1hu5d02ptja2duic3saa60qgolal9...(a)4ax.com>, > Roedy Green <see_webs...(a)mindprod.com.invalid> wrote: > > > Can anyone think of a way to write a method that takes an array of X, > > and produces a HashMap<Key,X> > > > How would you specify the name of the key field/method? > > > Maybe you could do it by making X implement an interface that defines > > the key. > > > Perhaps you could do it with reflection. > > If the key is easily derived from the value, what you might have is a > set rather than a map. If that's not the case, Generics trickery will > make it a simple task. Good point. Although I believe that could not be stated for the most general case. > interface Key > { > boolean equals(Object other); > int hashCode (); > // Other stuff > > } > > interface Keyed > { > Key getKey(); > > } > > public <K extends Keyed> HashMap <Key, K> keyedMap (K[] in) > { > HashMap<Key, K> map= new HashMap<Key, K>(in.length); > for (K keyed : in) > map.put(keyed.getKey(), keyed); > return map; > > } > > You could even put generics on they Key to support different categories > of keys. It would be nearing the boundary of where Generics stops > making coding easier, though. Roedy did not state how exactly the key is obtained for a particular value although he seems to insinuate that it's a property of the value. I prefer a more modular approach where the key finding algorithm is completely decoupled from the value type. This is much more modular and allows for better reuse. Here are some advantages: - If you have multiple key candidates making the value type implement a particular interface limits you to using exactly one of those candidates. Or you have to wrap values in another type which implements the key extraction interface which has all sorts of nasty effects because object identity changes - and it might also be slower, since more objects are needed. - You can obtain keys for items which do not exhibit matching properties at all. - You can even create a completely new key object based on arbitrary state which can be extracted from the value object or from somewhere else (e.g. a counter). Note also how I have separated Map creation from Map filling in order to retain even greater modularity of the code. That way you can apply the key extraction and map filling algorithm to even more Map types than only HashMap. You can even fill a single map from multiple value sources. Kind regards robert
From: Tom Anderson on 11 May 2010 08:36 On Tue, 11 May 2010, Robert Klemme wrote: > On 11.05.2010 00:20, Roedy Green wrote: >> Can anyone think of a way to write a method that takes an array of X, >> and produces a HashMap<Key,X> >> >> How would you specify the name of the key field/method? >> >> Maybe you could do it by making X implement an interface that defines >> the key. >> >> Perhaps you could do it with reflection. > > I'd rather provide an interface that is responsible for the conversion from X > to Key - this is more modular. That's what i'd do too. Many winters ago, i faced the same problem, and ended up doing it the interface way - i required my elements to implement: public interface Keyed { public Object getKey(); // this was before generics! } And then wrote code to build a map using the keys derived from supplied values. Having had years to digest that design, i now think a separate key-derivation function is a better idea. Incidentally, you could use Robert's approach to wrap the reflective approach easily: public class KeyExtractor<A, B> implements Transformer<A, B> { private Method getter; private KeyExtractor(Class<A> a, String methodName, Class<B> b) { Method method = a.getMethod(methodName); if (!b.isAssignableFrom(method.getReturnType())) throw new IllegalArgumentException("bad return type from getter"); this.getter = method; } @SuppressWarnings("unchecked") public B transform (A obj) { return (B)method.invoke(obj); } } Now you can say: Customer[] customers; MapUtil.createHash(customers, new KeyExtractor(Customers.class, "getSalesman", Salesman.class)); Which is amazingly wordy, so maybe you wouldn't bother. I initially thought Roedy wanted a method to create maps from explicit lists of keys and values, and wrote this: public static <K, V> Map<K, V> mapWith(K[] keys, V[] values) { if (keys.length != values.length) throw new IllegalArgumentException("different number of keys and values"); Map<K, V> map = new LinkedHashMap<K, V>(keys.length); for (int i = 0; i < keys.length; ++i) { map.put(keys[i], values[i]); } return map; } public static <K> K[] keys(K... keys) { return keys; } public static <V> V[] values(V... values) { return values; } example after suitable static imports: mapWith(keys(1, 2, 3), values("one", "two", "three")) But that's not what he wanted. tom -- But in the week its like Urbino under the wise rule of Count Federico, only with a better football team and the nations most pleb-infested Waitrose. And shops selling size 12 stilettos. -- Jelb, on Holloway
From: Daniel Pitts on 11 May 2010 18:18 On 5/10/2010 3:20 PM, Roedy Green wrote: > Can anyone think of a way to write a method that takes an array of X, > and produces a HashMap<Key,X> > > How would you specify the name of the key field/method? > > Maybe you could do it by making X implement an interface that defines > the key. > public class Table<R> { private final List<UniqueIndex<?, R>> indexes = new ArrayList<UniqueIndex<?, R>>(); public <K> UniqueIndex<K, R> addIndex(KeyExtractor<K, R> extractor) { // create index and add to indexes } public void add(R item) { for(UniqueIndex<?, R> index: indexes) { index.add(item); } } } public class UniqueIndex<K, R> { private final KeyExtractor<K, R> extractor; private final Map<K, R> map = new HashMap<K, R>(); public R add(R item) { return map.put(extractor.extract(item), item); } public Map<K, R> getMap() { /* return copy */ } } public interface KeyExtractor<K, R> { K extract(R); } > Perhaps you could do it with reflection. Don't use reflection unless you have a really good reason. I would prefer to use code generation before I use reflection in this situation. -- Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
From: Lew on 12 May 2010 00:11
Roedy Green writes: >> Can anyone think of a way to write a method that takes an >> array of X, and produces a HashMap<Key,X> Stefan Ram wrote: > HashMap< Key, X>m( final X[] x ){ return new HashMap< Key, X>(); } That is genius. This is my favorite answer so far. Most of the other answers just made my head hurt. I wonder what the point is. Rephrasing, what is the value? -- Lew |