Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / August 2007

Tip: Looking for answers? Try searching our database.

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

Thread view: 
robertjparks@gmail.com - 03 Aug 2007 19:13 GMT
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
Thomas Hawtin - 03 Aug 2007 20:03 GMT
> 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
[quoted text clipped - 46 lines]
> 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
robertjparks@gmail.com - 03 Aug 2007 22:31 GMT
> robertjpa...@gmail.com wrote:
> > Hi, I make extensive use of N-dimensional Maps in my code and would
[quoted text clipped - 86 lines]
>
> 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
Patricia Shanahan - 03 Aug 2007 22:37 GMT
>> robertjpa...@gmail.com wrote:
>>> Hi, I make extensive use of N-dimensional Maps in my code and would
[quoted text clipped - 87 lines]
> 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
Mark Space - 04 Aug 2007 02:01 GMT
> 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...
Thomas Hawtin - 04 Aug 2007 04:05 GMT
> 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
Twisted - 04 Aug 2007 04:16 GMT
> > 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.
Thomas Hawtin - 04 Aug 2007 04:40 GMT
[ >> Mark Space wrote: ]
>>> class MyMap extends Map
>> I guess that would be extends HashMap. Perhaps introduce an interface
[quoted text clipped - 6 lines]
>
> 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


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.