Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > How to use java.util.Map in a more Perl like way.

Reply
Thread Tools

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

 
 
robertjparks@gmail.com
Guest
Posts: n/a
 
      08-03-2007
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

 
Reply With Quote
 
 
 
 
Thomas Hawtin
Guest
Posts: n/a
 
      08-03-2007
http://www.velocityreviews.com/forums/(E-Mail Removed) 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
 
Reply With Quote
 
 
 
 
robertjparks@gmail.com
Guest
Posts: n/a
 
      08-03-2007
On Aug 3, 3:07 pm, Thomas Hawtin <(E-Mail Removed)> wrote:
> (E-Mail Removed) 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

 
Reply With Quote
 
Patricia Shanahan
Guest
Posts: n/a
 
      08-03-2007
(E-Mail Removed) wrote:
> On Aug 3, 3:07 pm, Thomas Hawtin <(E-Mail Removed)> wrote:
>> (E-Mail Removed) 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
 
Reply With Quote
 
Mark Space
Guest
Posts: n/a
 
      08-04-2007
(E-Mail Removed) 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...
 
Reply With Quote
 
Thomas Hawtin
Guest
Posts: n/a
 
      08-04-2007
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
 
Reply With Quote
 
Twisted
Guest
Posts: n/a
 
      08-04-2007
On Aug 3, 11:07 pm, Thomas Hawtin <(E-Mail Removed)> 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.

 
Reply With Quote
 
Thomas Hawtin
Guest
Posts: n/a
 
      08-04-2007
Twisted wrote:
> On Aug 3, 11:07 pm, Thomas Hawtin <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
Like all great travelers, I have seen more than I remember andremember more than I have seen. shenrilaa@gmail.com C Programming 0 03-05-2008 03:26 AM
Parse tree like data like XML by Perl? Davy Perl Misc 2 10-09-2006 04:30 AM
object-like macro used like function-like macro Patrick Kowalzick C++ 5 03-14-2006 03:30 PM
DVD Verdict reviews: LIKE FATHER, LIKE SON, THE CODE, and more! DVD Verdict DVD Video 0 06-18-2004 09:03 AM



Advertisments