How to use java.util.Map in a more Perl like way.

Discussion in 'Java' started by robertjparks@gmail.com, Aug 3, 2007.

  1. Guest

    Hi, I make extensive use of N-dimensional Maps in my code and would
    like to find out if there is a way to manipulate them in a more
    Perlish fashion. For example, say I have 2D map and I want to write
    all the way through to the end. My code else up looking like this:

    Map<String, Map<String,String>> map = new HashMap<String,
    Map<String,String>>();
    String key1= "key1";
    String key2="key2";
    String val="val";
    // write to the structure building it up as you go
    if(!map.containsKey(key1)) map.put(key1,new HashMap<String,String>());
    if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);

    In perl, you don't have to build up and walk through the structure in
    order to write to it. For example, this would suffice:
    my %map=();
    my $key1= "key1";
    my $key2="key2";
    my $val="val";
    # write to the structure in 1 shot
    map{$key1}{$key2}=$val

    To avoid having to "walk through and build up the structure" every
    time I write to it, I wrote a static MapUtils to do the it. You can
    say I am lazy here but the walking code blows out really fast when you
    have an 5 level deep Map and I like to keep things short and neat.

    public MapUtils{
    public static put(Map<String,Map<String,String>> map, String key1,
    String, key2,String, val){
    if(!map.containsKey(key1)) map.put(key1,new
    HashMap<String,String>());
    if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);
    }
    }

    Now I can just say:

    MapUtils.put(map, key1, key2, val);

    Which make my code much more readable.

    Now here is where I need help!

    How do I write MapUtils.put() so that it can take a Map<?,?> of any
    number of dimensions and types and a list of N-keys and 1 value of any
    type? I tried messing around with generics and wildcards but didn't
    get too far. Maybe what I want to do is not possible. If this is the
    case, I would like to hear why.

    So far my best solution is to write a new version of put() every time
    I need to write to a new type of Map and let function overloading pick
    the correct one. This works ok, but I am hoping somone can offer a
    better approach.

    Thanks,
    Rob
    , Aug 3, 2007
    #1
    1. Advertising

  2. wrote:
    > Hi, I make extensive use of N-dimensional Maps in my code and would
    > like to find out if there is a way to manipulate them in a more
    > Perlish fashion. For example, say I have 2D map and I want to write
    > all the way through to the end. My code else up looking like this:
    >
    > Map<String, Map<String,String>> map = new HashMap<String,
    > Map<String,String>>();
    > String key1= "key1";
    > String key2="key2";
    > String val="val";
    > // write to the structure building it up as you go
    > if(!map.containsKey(key1)) map.put(key1,new HashMap<String,String>());
    > if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);
    >
    > In perl, you don't have to build up and walk through the structure in
    > order to write to it. For example, this would suffice:
    > my %map=();
    > my $key1= "key1";
    > my $key2="key2";
    > my $val="val";
    > # write to the structure in 1 shot
    > map{$key1}{$key2}=$val
    >
    > To avoid having to "walk through and build up the structure" every
    > time I write to it, I wrote a static MapUtils to do the it. You can
    > say I am lazy here but the walking code blows out really fast when you
    > have an 5 level deep Map and I like to keep things short and neat.
    >
    > public MapUtils{
    > public static put(Map<String,Map<String,String>> map, String key1,
    > String, key2,String, val){
    > if(!map.containsKey(key1)) map.put(key1,new
    > HashMap<String,String>());
    > if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);
    > }
    > }
    >
    > Now I can just say:
    >
    > MapUtils.put(map, key1, key2, val);
    >
    > Which make my code much more readable.
    >
    > Now here is where I need help!
    >
    > How do I write MapUtils.put() so that it can take a Map<?,?> of any
    > number of dimensions and types and a list of N-keys and 1 value of any
    > type? I tried messing around with generics and wildcards but didn't
    > get too far. Maybe what I want to do is not possible. If this is the
    > case, I would like to hear why.


    Instead of your put, you could write a static get method that creates if
    necessary.

    import static collection.HashMaps.get;
    ....
    Map<String,Map<String,String>> map2;
    Map<String,Map<String,Map<String,String>>> map3;
    ...
    get(map2, key1).put(key2, value);
    get(get(map3, key1), key2).put(key3, value);
    ....

    package collection;

    public final class HashMaps {
    private Maps() {
    throw new Error();
    }
    public static <K, MK, MV> Map<MK, MV> get(
    Map<K, Map<MK, MV>> map, K key
    ) {
    Map<MK, MV> nested = map.get(key);
    if (nested == null) {
    nested = new java.util.HashMap<MK, MV>();
    map.put(key, nested);
    }
    return nested;
    }
    }

    Perhaps better would be to write your own Map-like types to create on
    demand.

    Another approach is to use a single map with composite key. That also
    may be faster and more memory efficient.

    Tom Hawtin
    Thomas Hawtin, Aug 3, 2007
    #2
    1. Advertising

  3. Guest

    On Aug 3, 3:07 pm, Thomas Hawtin <> wrote:
    > wrote:
    > > Hi, I make extensive use of N-dimensional Maps in my code and would
    > > like to find out if there is a way to manipulate them in a more
    > > Perlish fashion. For example, say I have 2D map and I want to write
    > > all the way through to the end. My code else up looking like this:

    >
    > > Map<String, Map<String,String>> map = new HashMap<String,
    > > Map<String,String>>();
    > > String key1= "key1";
    > > String key2="key2";
    > > String val="val";
    > > // write to the structure building it up as you go
    > > if(!map.containsKey(key1)) map.put(key1,new HashMap<String,String>());
    > > if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);

    >
    > > In perl, you don't have to build up and walk through the structure in
    > > order to write to it. For example, this would suffice:
    > > my %map=();
    > > my $key1= "key1";
    > > my $key2="key2";
    > > my $val="val";
    > > # write to the structure in 1 shot
    > > map{$key1}{$key2}=$val

    >
    > > To avoid having to "walk through and build up the structure" every
    > > time I write to it, I wrote a static MapUtils to do the it. You can
    > > say I am lazy here but the walking code blows out really fast when you
    > > have an 5 level deep Map and I like to keep things short and neat.

    >
    > > public MapUtils{
    > > public static put(Map<String,Map<String,String>> map, String key1,
    > > String, key2,String, val){
    > > if(!map.containsKey(key1)) map.put(key1,new
    > > HashMap<String,String>());
    > > if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);
    > > }
    > > }

    >
    > > Now I can just say:

    >
    > > MapUtils.put(map, key1, key2, val);

    >
    > > Which make my code much more readable.

    >
    > > Now here is where I need help!

    >
    > > How do I write MapUtils.put() so that it can take a Map<?,?> of any
    > > number of dimensions and types and a list of N-keys and 1 value of any
    > > type? I tried messing around with generics and wildcards but didn't
    > > get too far. Maybe what I want to do is not possible. If this is the
    > > case, I would like to hear why.

    >
    > Instead of your put, you could write a static get method that creates if
    > necessary.
    >
    > import static collection.HashMaps.get;
    > ...
    > Map<String,Map<String,String>> map2;
    > Map<String,Map<String,Map<String,String>>> map3;
    > ...
    > get(map2, key1).put(key2, value);
    > get(get(map3, key1), key2).put(key3, value);
    > ...
    >
    > package collection;
    >
    > public final class HashMaps {
    > private Maps() {
    > throw new Error();
    > }
    > public static <K, MK, MV> Map<MK, MV> get(
    > Map<K, Map<MK, MV>> map, K key
    > ) {
    > Map<MK, MV> nested = map.get(key);
    > if (nested == null) {
    > nested = new java.util.HashMap<MK, MV>();
    > map.put(key, nested);
    > }
    > return nested;
    > }
    >
    > }
    >
    > Perhaps better would be to write your own Map-like types to create on
    > demand.
    >
    > Another approach is to use a single map with composite key. That also
    > may be faster and more memory efficient.
    >
    > Tom Hawtin


    Thanks for the feedback.

    I agree that having get("missingKey") automatically build out the Map
    will make it work more like perl and also will solve my put() issue.

    The problem is that I shouldn't have said I wanted it to work EXACTLY
    like perl. I like that in perl you can write through hash dimensions
    and put them in existence, but I don't like that when you read through
    a missing hash key that it adds it automatically. For example, I don't
    like when I check

    if(exists($map{"k1"}{"k2"})){ ... }

    that it puts "k1" into existence. So although your suggestion is great
    for emulating perl, it isn't what I was looking for.

    The composite key is also a good idea, but it doesn't quite have the
    same functionality. Although I always write through all the dimensions
    of the Map, I still like that multi-dimensional maps allow you to see
    all the values for a specific key.

    So thanks for the work around suggestions, but I am still hoping to
    solve my exact problem.

    Thanks,
    Rob
    , Aug 3, 2007
    #3
  4. wrote:
    > On Aug 3, 3:07 pm, Thomas Hawtin <> wrote:
    >> wrote:
    >>> Hi, I make extensive use of N-dimensional Maps in my code and would
    >>> like to find out if there is a way to manipulate them in a more
    >>> Perlish fashion. For example, say I have 2D map and I want to write
    >>> all the way through to the end. My code else up looking like this:
    >>> Map<String, Map<String,String>> map = new HashMap<String,
    >>> Map<String,String>>();
    >>> String key1= "key1";
    >>> String key2="key2";
    >>> String val="val";
    >>> // write to the structure building it up as you go
    >>> if(!map.containsKey(key1)) map.put(key1,new HashMap<String,String>());
    >>> if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);
    >>> In perl, you don't have to build up and walk through the structure in
    >>> order to write to it. For example, this would suffice:
    >>> my %map=();
    >>> my $key1= "key1";
    >>> my $key2="key2";
    >>> my $val="val";
    >>> # write to the structure in 1 shot
    >>> map{$key1}{$key2}=$val
    >>> To avoid having to "walk through and build up the structure" every
    >>> time I write to it, I wrote a static MapUtils to do the it. You can
    >>> say I am lazy here but the walking code blows out really fast when you
    >>> have an 5 level deep Map and I like to keep things short and neat.
    >>> public MapUtils{
    >>> public static put(Map<String,Map<String,String>> map, String key1,
    >>> String, key2,String, val){
    >>> if(!map.containsKey(key1)) map.put(key1,new
    >>> HashMap<String,String>());
    >>> if(!map.get(key1).containsKey(key2)) map.get(key1).put(key2,val);
    >>> }
    >>> }
    >>> Now I can just say:
    >>> MapUtils.put(map, key1, key2, val);
    >>> Which make my code much more readable.
    >>> Now here is where I need help!
    >>> How do I write MapUtils.put() so that it can take a Map<?,?> of any
    >>> number of dimensions and types and a list of N-keys and 1 value of any
    >>> type? I tried messing around with generics and wildcards but didn't
    >>> get too far. Maybe what I want to do is not possible. If this is the
    >>> case, I would like to hear why.

    >> Instead of your put, you could write a static get method that creates if
    >> necessary.
    >>
    >> import static collection.HashMaps.get;
    >> ...
    >> Map<String,Map<String,String>> map2;
    >> Map<String,Map<String,Map<String,String>>> map3;
    >> ...
    >> get(map2, key1).put(key2, value);
    >> get(get(map3, key1), key2).put(key3, value);
    >> ...
    >>
    >> package collection;
    >>
    >> public final class HashMaps {
    >> private Maps() {
    >> throw new Error();
    >> }
    >> public static <K, MK, MV> Map<MK, MV> get(
    >> Map<K, Map<MK, MV>> map, K key
    >> ) {
    >> Map<MK, MV> nested = map.get(key);
    >> if (nested == null) {
    >> nested = new java.util.HashMap<MK, MV>();
    >> map.put(key, nested);
    >> }
    >> return nested;
    >> }
    >>
    >> }
    >>
    >> Perhaps better would be to write your own Map-like types to create on
    >> demand.
    >>
    >> Another approach is to use a single map with composite key. That also
    >> may be faster and more memory efficient.
    >>
    >> Tom Hawtin

    >
    > Thanks for the feedback.
    >
    > I agree that having get("missingKey") automatically build out the Map
    > will make it work more like perl and also will solve my put() issue.
    >
    > The problem is that I shouldn't have said I wanted it to work EXACTLY
    > like perl. I like that in perl you can write through hash dimensions
    > and put them in existence, but I don't like that when you read through
    > a missing hash key that it adds it automatically. For example, I don't
    > like when I check


    You may still be able to use the HashMaps idea, but give it two distinct
    get methods, a pure get and a creatingGet. creatingGet would be the get
    shown above. The pure get would check for nesting, but return null
    rather than creating a new mapping. In a put situation you would use the
    creatingGet.

    Patricia
    Patricia Shanahan, Aug 3, 2007
    #4
  5. Mark Space Guest

    wrote:

    > Now I can just say:
    >
    > MapUtils.put(map, key1, key2, val);


    Whenever I see this type of construct:

    static procedure( Object thing_to_operate_on, ... )

    I think somebody missed an opportunity for inheritance

    class MyMap extends Map
    {
    //...

    void procedure( ... )
    //..
    }

    But I guess that's kind of obvious too. I like the idea of extending
    the methods, rather than over-riding them, which is a tad safer and less
    complicated in general. A new get() and put() might not be to hard. I
    like Patricia's suggestion to add a creatingGet() method, that would
    work well. Var args could clean up the code to add variable numbers of
    keys too...
    Mark Space, Aug 4, 2007
    #5
  6. Mark Space wrote:
    >
    > I think somebody missed an opportunity for inheritance


    I think you should prefer not to use inheritance. It's a big powerful
    tool, but you probably don't want to throw it around too eagerly.

    > class MyMap extends Map


    I guess that would be extends HashMap. Perhaps introduce an interface
    that extends Map and a class that extends HashMap and implements the
    interface.

    That doesn't work so well if this is an isolated piece of code and the
    map comes from elsewhere. So you might want to use a decorator. But a
    decorator to do something like this feels a bit clumsy. It's almost as
    bad as creating a new object only to call a single method on it.

    > work well. Var args could clean up the code to add variable numbers of
    > keys too...


    But then you'd lose type safety and some performance.

    If you still wanted that approach, there would be less code to simply
    use java.util.Arrays.asList to create the keys. I don't recommend it.

    Tom Hawtin
    Thomas Hawtin, Aug 4, 2007
    #6
  7. Twisted Guest

    On Aug 3, 11:07 pm, Thomas Hawtin <> wrote:
    > > class MyMap extends Map

    >
    > I guess that would be extends HashMap. Perhaps introduce an interface
    > that extends Map and a class that extends HashMap and implements the
    > interface.


    Ugh!!

    Try

    public class MyMap<K,V> implements Map<K,V>
    private Map<K,V> delegate;
    public MyMap () {
    delegate = new HashMap<K,V>();
    }
    public MyMap (Map<K,V> delegate) {
    this.delegate = delegate;
    }
    ...
    }

    This uses a HashMap by default but lets people use the alt constructor
    to dependency-inject and get a MyMap based on a TreeMap, etc. (and of
    course they can specify a TreeMap comparator when they construct the
    TreeMap prior to passing it to the MyMap constructor). The one iffy
    thing is the aliasing that occurs if the TreeMap (or whatever)
    reference is kept around and also gets used; changes to the MyMap and
    the TreeMap are reflected in one another.

    To OP: If the key types where you want multiple keys are always all
    the same, just use Map<List<KeyType>, ValueType>; this supports
    different key list lengths for different entries. If you don't want
    that make a fixed-size immutable list class; e.g. use
    Map<ThreeElementList<KeyType>, ValueType> and make ThreeElementList a
    class that implements List and has a constructor that accepts a List
    but throws if it's the wrong length, otherwise wrapping it and
    implementing none of the "optional" (list-mutating) operations.
    Twisted, Aug 4, 2007
    #7
  8. Twisted wrote:
    > On Aug 3, 11:07 pm, Thomas Hawtin <> wrote:

    [ >> Mark Space wrote: ]
    >>> class MyMap extends Map

    >> I guess that would be extends HashMap. Perhaps introduce an interface
    >> that extends Map and a class that extends HashMap and implements the
    >> interface.

    >
    > Ugh!!
    >
    > Try
    >
    > public class MyMap<K,V> implements Map<K,V>


    Yes, that's a decorator as mentioned in my previous post. It's a lot
    more work (although the method forwarding can be factored out into an
    abstract class).

    Tom Hawtin
    Thomas Hawtin, Aug 4, 2007
    #8
    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. Donna
    Replies:
    1
    Views:
    941
    Stefan Schulz
    Feb 7, 2005
  2. Replies:
    5
    Views:
    6,336
  3. Christopher Benson-Manica

    Using java.util.map

    Christopher Benson-Manica, Aug 15, 2006, in forum: Java
    Replies:
    20
    Views:
    9,071
    Robert Klemme
    Aug 16, 2006
  4. santax
    Replies:
    0
    Views:
    458
    santax
    May 7, 2007
  5. MooMaster
    Replies:
    12
    Views:
    12,296
    Tom Anderson
    Nov 9, 2008
Loading...

Share This Page