Java Forum / General / August 2007
How to use java.util.Map in a more Perl like way.
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 MagazinesGet 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 ...
|
|
|