views:

153

answers:

7

In my java coding, I often end up with several Map<String,Map<String,foo>> or Map<String,List<String>> and then I have trouble remembering which String is which key. I comment the declaration with //Map<capabiltyId,Map<groupId,foo>> or //Map<groupId,List<capabilityId>, but it's not the greatest solution. If String wasn't final, I would make new classes CapabilityId extends String and GroupId extends String, but I can't. Is there a better way to keep track of which thing is the key and maybe have the compiler enforce it?

+9  A: 

Wrap strings in wrapper-classes if you want:

class GroupId implements Comparable {
   private String groupId;

   public GroupId (String groupId) {
       this.groupId = groupId;
   }
   ...
}

Map<GroupId, List<CapabilityId>> m = ...
Roman
Why implement Comparable?
Steve Kuo
+3  A: 

Create an ID class which you can subclass, and which consists of a String field and implementations of equals() and hashCode() which use that field.

Michael Borgwardt
+7  A: 

Instead of having CapabilityId extend String, CapabilityId could include a String field called "id"; then your Map could be defined as Map<CapabilityId, Map<GroupId, Foo>>, and you could get at the individual ID fields through a getId() on your key classes.

I'm not sure I would do this myself, but if I did, this is probably what I'd do.

You could limit the clutter by having an abstract GenericId class with an id field and getId() method, and have CapabilityId and GroupId inherit from it.

Jim Kiley
That's a very Lincoln-esque comment.
Paul Tomblin
I admit I was going for the Jerry Pournelle "If you need this, you need it bad," but in reverse.
Jim Kiley
This is exactly what I did yesterday in order to track down a memory leak; instead of 150m String objects I could instantly spot which map was not being cleared down by seeing how many nnnnKey objects were being created.
rhu
I don't like wrapping. It means the objects have another pointer, which is a waste of space for something that should be syntactic only.
shoren
In the year 2010, the existence of another few bytes of pointer is going to have no impact on performance. And it will greatly clarify your code, both for yourself and for other developers. This will in turn speed development and maintenance time. Both of those are expensive. Bytes are cheap.
Jim Kiley
A: 

Instead of Map<String,List<String>> you should use Multimap from Google Guava / Google Collection

http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/Multimap.html

nanda
A: 

Adding to the other answers:

Wrap it.

It is not just a solution to your problem but a good idea in general, i.e. avoid simple parameters. Your code will gain readability, sanity and maintainability. You can add all kinds of nice properties to it, e.g. declare it @Immutable. As you found out it this way is better to remember and to control. You own the class and can do whatever you like with it.

openCage
+2  A: 

I would put it all in single class and make use of sensible field/method/argument names.

public class GroupCapabilities {
    private Map<String, Map<String, Group>> groupCapabilities;

    public void addGroup(String capabilityId, Group group) {
        Map<String, Group> groups = groupCapabilities.get(capabilityId);
        if (groups = null) {
            groups = new HashMap<String, Group>();
            groupCapabilities.put(capabilityId, group);
        }
        groups.put(group.getId(), group);
    }

    public Map<String, Group> getGroups(String capabilityId) {
        return groupCapabilities.get(capabilityId);
    }

    public Group getGroup(String capabilityId, String groupId) {
        Map<String, Group> groups = groupCapabilities.get(capabilityId);
        return (groups != null) ? groups.get(groupId) : null;
    }

    // Etc..
}

This way the you can see at method/argument names what it expects/returns.

BalusC
+1  A: 

There are a number of ways to go on this one (some already mentioned):

  • As @Roman, wrap the general purpose type in a more specific type, which gives stronger typing. Strong typing good, IMO.
  • As @nanda, use a more specific collection type. The Java library is a little poor in this area. It depends about how you feel about dependencies.
  • As @BalusC, move all the icky stuff into an icky class. Doesn't really remove the problem, but it does contain it (like in Ghostbusters).
  • Map<String,Map<String,foo>> looks very much like you have a composite key, i.e. a key that comprises two parts. So, introduce an immutable composite key class, that is a value object representing the two component value objects.
Tom Hawtin - tackline