views:

359

answers:

3

Hi,

I'm using a recursive tree of hashmaps, specifically Hashmap map where Object is a reference to another Hashmap and so on. This will be passed around a recursive algorithm:

foo(String filename, Hashmap<String, Object> map)
{
    //some stuff here
    for (Entry<String, Object> entry : map.entrySet()) 
    {
       //type warning that must be suppressed
       foo(entry.getKey(), (HashMap<String, Object>)entry.getValue());
    }
}

I know for sure Object is of type Hashmap<String, Object> but am irritated that I have to suppress the warning using @SuppressWarnings("unchecked").

I'll be satisfied with a solution that does either a assert(/*entry.getValue() is of type HashMap<String, Object>*/) or throws an exception when it isn't. I went down the Generics route for compile type safety and if I suppress the warning then it defeats the purpose.

Thank you for your comments, ksb

+4  A: 

You can use this class instead of HashMap:

public class RecursiveHashMap extends HashMap<String,RecursiveHashMap>
{
}
Ha
Dear Ha,Don't really understand why this might work (Java noob), therefore didn't try it out. I'm going with waxwing's Node solution.Thanks,KSB
GC
Yeap, creating `Node` class (aka Composite pattern) is way better than `HashMap`'s.
Ha
+5  A: 

This is possible using a generic method with a recursive type variable. Try the following:

public <T extends Map<String, T>> void foo(String filename, T map) {
    //some stuff here
    for (Map.Entry<String, T> entry : map.entrySet())  {
        foo(entry.getKey(), entry.getValue());
    }
}

Should compile fine without any warnings.

However, if you have control of the map, and can substitute your own class, it might be more readable to make a class Node (this looks like a tree to me), that contains a Map instead. Something like:

public class Node {
    private Map<String, Node> children;

    ...
    // accessor methods to retrieve children ...
}

And have foo take a Node as its second argument instead. Just a suggestion.

waxwing
dear waxwing,I ended up implementing your second suggestion because it turns out that I need to add additional things to the "node" than just a reference to another hashmap. It also feels much more natural.I'm just getting to grips with Java, so can't understand your first suggestion - the "T extends Map<String,T>" part.Thank you once again,KSB
GC
+1  A: 

Your data structure looks like you want to represent trees of files (filenames) with it. I'd not recommend to do this with HashMap as the node type.

I'd suggest to use the composite pattern (see wikipedia), simplified code:

abstract class Node
{
  String filename;
  Node( String filename ) { this.filename = filename; }
  abstract foo();
}

class FileNode implements Node
{
  FileNode( String filename ) { super(filename); }
  foo() { ... }
}

class DirectoryNode implements Node 
{
  Set<Node> children;
  DirectoryNode( String filename, Set<Node> children )
  {
    super(filename);
    this.children = children;
  }
  foo()
  {
    for ( Node child : children ) child.foo();
  }
}

The HashMap you were using boils down to the Set appearing in DirectoryNode.

Wolfgang
Thanks for the thought. My entities can't be naturally partitioned into two node types i.e. Directory and Files. All of them are "Files". Specifically I need to do the same processing foo() at all layers of the hierarchy. I'm not able to extend your code to accomplish this.
GC
Why not? You can put all the code you need into DirectoryNode.foo(). You could also put the code into Node.foo() and then call super.foo() on DirectoryNode.foo(). Then Node would be your File.
Wolfgang
I see what you're saying: Put the common code inside Node (foo isn't abstract anymore) and have File and Directory call super(filename). Will remember that one.Thanks, ksb
GC