Encapsulating HashMap bulding

Discussion in 'Java' started by Roedy Green, May 10, 2010.

  1. Roedy Green

    Roedy Green Guest

    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.
    --
    Roedy Green Canadian Mind Products
    http://mindprod.com

    What is the point of a surveillance camera with insufficient resolution to identify culprits?
     
    Roedy Green, May 10, 2010
    #1
    1. Advertising

  2. Roedy Green

    Lew Guest

    On 05/10/2010 06: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.
    >
    > Perhaps you could do it with reflection.


    Map <Key, X> map = new HashMap <Key, X>();
    for ( X x : arrayOfX )
    {
    map.put( genKey( x ), x );
    }

    Example:

    public class X
    {
    private final Key key; // getter and setter omitted
    public X( Key k ) { key = k; }
    public Key getKey() { return key; }
    // other attributes omitted
    }

    other class#method:

    ...
    for ( X x : arrayOfX )
    {
    map.put( x.getKey(), x );
    }

    --
    Lew
     
    Lew, May 10, 2010
    #2
    1. Advertising

  3. Roedy Green

    Arne Vajhøj Guest

    On 10-05-2010 18: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.


    The two approaches seems to be what is available.

    See below for some code.

    Arne

    =============================

    import java.beans.IntrospectionException;
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.Map;

    public class A2HM {
    public static <K,T extends KeyContainer<K>> Map<K,T> convert1(T[] a) {
    Map<K,T> res = new HashMap<K,T>();
    for(T e : a) {
    res.put(e.getKey(), e);
    }
    return res;
    }
    @SuppressWarnings("unchecked")
    public static <K,T> Map<K,T> convert2(T[] a, String prop, Class<K>
    clz) throws IntrospectionException, IllegalArgumentException,
    IllegalAccessException, InvocationTargetException {
    PropertyDescriptor pd = new PropertyDescriptor(prop,
    a.getClass().getComponentType());
    Map<K,T> res = new HashMap<K,T>();
    for(T e : a) {
    res.put((K)pd.getReadMethod().invoke(e), e);
    }
    return res;
    }
    public static void main(String[] args) throws Exception {
    Foobar[] a = new Foobar[3];
    a[0] = new Foobar(1,1.2,"A");
    a[1] = new Foobar(2,12.34,"BB");
    a[2] = new Foobar(3,123.456,"CCC");
    System.out.println(convert1(a));
    System.out.println(convert2(a, "k", Integer.class));
    }
    }

    interface KeyContainer<K> {
    public K getKey();
    }

    class Foobar implements KeyContainer<Integer> {
    private int k;
    private double v1;
    private String v2;
    public Foobar(int k, double v1, String v2) {
    this.k = k;
    this.v1 = v1;
    this.v2 = v2;
    }
    public int getK() {
    return k;
    }
    public void setK(int k) {
    this.k = k;
    }
    public double getV1() {
    return v1;
    }
    public void setV1(double v1) {
    this.v1 = v1;
    }
    public String getV2() {
    return v2;
    }
    public void setV2(String v2) {
    this.v2 = v2;
    }
    public Integer getKey() {
    return k;
    }
    @Override
    public String toString() {
    return "(" + k + "," + v1 + "," + v2 + ")";
    }
    }
     
    Arne Vajhøj, May 11, 2010
    #3
  4. 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.

    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<V, K> t) {
    return createHash(Arrays.asList(i), t);
    }

    public static <K, V> Map<K, V> createHash(Iterable<V> i,
    Transformer<V, 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<V> i,
    Transformer<V, K> t) {
    for (V val : i) {
    m.put(t.transform(val), val);
    }
    }
    }

    Kind regards

    robert

    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, May 11, 2010
    #4
  5. In article <>,
    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.


    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.


    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.
    --
    I won't see Google Groups replies because I must filter them as spam
     
    Kevin McMurtrie, May 11, 2010
    #5
  6. 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/
     
    Robert Klemme, May 11, 2010
    #6
  7. On May 11, 8:08 am, Kevin McMurtrie <> wrote:
    > In article <>,
    >  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.

    >
    > 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
     
    Robert Klemme, May 11, 2010
    #7
  8. Roedy Green

    Tom Anderson Guest

    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, values);
    }
    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
     
    Tom Anderson, May 11, 2010
    #8
  9. Roedy Green

    Daniel Pitts Guest

    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/>
     
    Daniel Pitts, May 11, 2010
    #9
  10. Roedy Green

    Stefan Ram Guest

    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>


    HashMap< Key, X >m( final X[] x ){ return new HashMap< Key, X >(); }
     
    Stefan Ram, May 12, 2010
    #10
  11. Roedy Green

    Lew Guest

    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
     
    Lew, May 12, 2010
    #11
  12. On 12 Mai, 06:11, Lew <> wrote:
    > 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?


    I am sorry your brain hurts. ;-)

    Actually it did not occur to me that the code above is a valid
    implementation of the requirements before. Rereading Roedy's original
    posting I think it *is* a valid implementation although I must say
    that I found his question a bit vague. So, Roedy, what is it that you
    want?

    [ ] an empty HashMap with correct types
    [ ] a filled HashMap where keys are found based on the values in the
    array
    [ ] something else, namely
    _________________________________
    _________________________________

    Kind regards

    robert
     
    Robert Klemme, May 12, 2010
    #12
  13. On 12-05-2010 00:11, Lew wrote:
    > 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.


    I assume that it was a sarcastic comment on the level of precise
    description in Roedy's question.

    > Most of the other answers just made my head hurt. I wonder what the
    > point is. Rephrasing, what is the value?


    The point in Roedy's questions seems very clear to me. He want
    to reuse some code for a conversion that he apparently does
    frequently.

    Arne
     
    Arne Vajhøj, May 12, 2010
    #13
  14. Roedy Green

    Roedy Green Guest

    On Tue, 11 May 2010 02:22:07 -0700 (PDT), Robert Klemme
    <> wrote, quoted or indirectly quoted
    someone who said :

    >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 presumed the key was either a string field in the value or a get
    method on the value object for a string. For a somewhat more
    difficult problem, allow the key type to be an arbitrary Object of
    some generic type.
    --
    Roedy Green Canadian Mind Products
    http://mindprod.com

    Beauty is our business.
    ~ Edsger Wybe Dijkstra (born: 1930-05-11 died: 2002-08-06 at age: 72)

    Referring to computer science.
     
    Roedy Green, May 15, 2010
    #14
  15. Roedy Green

    Roedy Green Guest

    On 12 May 2010 03:14:50 GMT, -berlin.de (Stefan Ram)
    wrote, quoted or indirectly quoted someone who said :

    >>Can anyone think of a way to write a method that takes an
    >>array of X, and produces a HashMap<Key,X>

    >
    >HashMap< Key, X >m( final X[] x ){ return new HashMap< Key, X >(); }


    A lawyer's answer that answers the letter of the spec but not the
    spirit.
    --
    Roedy Green Canadian Mind Products
    http://mindprod.com

    Beauty is our business.
    ~ Edsger Wybe Dijkstra (born: 1930-05-11 died: 2002-08-06 at age: 72)

    Referring to computer science.
     
    Roedy Green, May 15, 2010
    #15
  16. Roedy Green

    Guest

    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>

    >


    Stefan Ram wrote, quoted or indirectly quoted someone who said :
    >>HashMap< Key, X >m( final X[] x ){ return new HashMap< Key, X >(); }

    >


    Roedy Green wrote:
    > A lawyer's answer that answers the letter of the spec but not the
    > spirit.
    >


    That's exactly what makes is so brilliant and funny.

    You could, should you have a sense of humor, also call it a comedian's
    answer that answers the letter of the spec but not the spirit.

    But yeah, there is that nasty precondition.

    Shakespeare wrote, "First, we kill all the lawyers" as a recipe for
    how to destabilize a society.

    --
    Lew
     
    , May 15, 2010
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Vince Darley
    Replies:
    4
    Views:
    4,431
    emilchacko
    Mar 2, 2010
  2. Andrew Degtiariov

    Bulding python module using C

    Andrew Degtiariov, Sep 24, 2004, in forum: Python
    Replies:
    2
    Views:
    251
    Andrew Degtiariov
    Sep 24, 2004
  3. hobel
    Replies:
    0
    Views:
    280
    hobel
    Sep 22, 2006
  4. Rakesh
    Replies:
    10
    Views:
    12,182
    Mike Schilling
    Apr 8, 2008
  5. Doug Livesey

    Advice on bulding a web service

    Doug Livesey, Aug 24, 2007, in forum: Ruby
    Replies:
    4
    Views:
    104
    Doug Livesey
    Aug 28, 2007
Loading...

Share This Page