1

I have a plain text file with some data that I can't change, so I have to create a datastructure to use in reading the file and with that datastructure do some thousands of interactions.

The file follows a defined pattern like this:

AAAAAA:zz_something sometxt1 sometxt2
AAAAAA:zz_something sometxt1 sometxt2
AAAAAA:zz_someotherthing sometxt1 sometxt2
BBBBBB:zz_something sometxt1 sometxt2
BBBBBB:zz_someotherthing sometxt1 sometxt2
BBBBBB:zz_something sometxt1 sometxt2
CCCCC:zz_others blahblah blehbleh
DDDDD:blihblih blohbloh
EEEEE:bluhbluh

This text is divided basically in three parts (most of it)

  • first, before the :, would be my object1
  • second, after the : AND IF it starts with zz_, would be my object2
  • third, everything else from that point on would be a List of strings
  • for the part after : that does not start with zz_ I will use a default string to identify it.

At first I thought of creating three objects with this structure:

 obj1                       obj2                 obj3
    String id                 String id            String str1
    Set<String> obj2          List<obj3> list      String str2

Plus a main object that would hold a Set<Obj1> main. But when I start to implement this structure I realize that it would be very difficult to read this in a loop to get any element inside it, like main.get('obj1').get('obj2').list, because it is a Set and I cannot do that.

So I ended up using a map as Map<String, Map<String, List<String>>> main but I feel like there must be a better way to do this.

What would be your recomendations?

2
  • How would you like to access the structure programmatically? While nested maps isn't wrong, it likely means there's a nice interface hiding there. Access to collection types has all the fun of null checks everywhere... and you will miss one at some point.
    – user40980
    Oct 21, 2014 at 23:04
  • I have a file with 40k+ lines and for each line i have to find the best match for each feature of this structure. The matches would be the "obj3->list". But isn't only that, for each file I identify what would be the best obj1 to use then from it get the proper obj2 then the list. I just feel like using a Map the code will not be elegant as it could. I would like to work with defined objects. So the code would be more readable
    – acidNoob
    Oct 21, 2014 at 23:12

1 Answer 1

2

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).

1
  • Thanks @MichaelT this is perfectly suitable for what I need! It is the elegant code that I was after :)
    – acidNoob
    Oct 22, 2014 at 1:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.