I come from a perl background. There, the combination of having autovivification and easy nested hash/list structures... the nested maps don't look too bad if you can keep your head around them. But on the other hand, they aren't that great either and it's really easy for a null pointer to creep in with other languages. In more formal languages, that you display your implementation makes refactoring a nightmare. It's certainly less than optimal or idiomatic Java code.
So, we've got three objects. They're nested. And that, while it can make it a bit awkward, you might want to look at actually making them nested classes.
One Two Three
AAAAAA:zz_something sometxt1 sometxt2
AAAAAA:zz_something sometxt1 sometxt2
AAAAAA:zz_someotherthing sometxt1 sometxt2
In this example, you would have a class One, Two, and Three. Class One would have an associated id of 'AAAAAA' and then other stuff. So you could have a definition akin to:
class One {
String id;
Map<String, Two> data;
public void One() { id = null; data = new HashMap(); }
public void build(String line) throws IllegalArgumentException {
parse line;
if (id == null) { id = parsedId; }
else { throw new IllegalArgumentException("attempt to reset id"); }
if data.contains(twoId) {
two = data.get(twoId);
} else {
two = new One.Two();
data.put(twoId, two);
}
two.build(line);
}
public static class Two {
// ....
private void build(String line) {
// ....
three = new One.Two.Three();
three.build(line);
}
}
}
Note that the Two.build
method is private. Because it's a nested class, the outer class can access the inner class private members and methods. This allows you to restrict the building of Two (and Three) to the public interface that One provides. This prevents other code from tinkering with it and messing it up later.
Furthermore, while this doesn't have all the access methods, they would be provided via the public interface of One in accordance with the Law of Demeter.
Yes, underneath this, this is a Map<String, Map<String, List<String>>>
- because that is what the data is. However, its one that doesn't disclose its implementation, or make a mess. Its still up to you to write methods of one.find("sometxt2")
which returns zz_something
if you want it, but it makes it much easier for the person using the object to do so. This in turn makes them less likely to make errors with the code (even if that person is you) and reduces the cognitive load allowing faster development and less worry about what is going on.
This also encourages dry code in that you won't be writing the code for find(String arg)
again and again and again each time you need it... or writing a 'helper' class with static methods that work on the data. When you find yourself writing SomeDataHelper classes or SomeDataUtil classes, you've likely got a class in there that wants to escape into a form akin to the one described above.
As an alternative to the nested class, one could use a package to hold the three classes and make the methods package protected (default protection - not public, or protected, or private) instead. This could make serialization easier and avoid what could very well be a java file that is three times longer (its three classes).