From: Robert Klemme on
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
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
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
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
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