Collection of Collections Is a Code Smell

From WikiContent

Revision as of 18:13, 19 April 2009 by Kcpeppe (Talk | contribs)
Jump to: navigation, search

Every once in a while I run into a design problem to which the answer is a HashMap of HashMaps or HashMap of ArrayList or some other combination of collection of collections. While some things, such a matrices, are naturally represented this way, more often than not, answer a design problem with a collection of collections isn't a natural representation. Instead it is an indication that you are missing a key abstraction. To translate this idea to code and you end up with a code smell.

The term code smell, first coined by Kent Beck, is used to describe code that is awkward or otherwise doesn't look quite right. Awkwardness in code tends to point to some deeper underlying problems either with the implementation or the design. In the case of a collection of collections, we often see an indication that one needs an abstraction to represent the relationship that is being expressed in the collection of collections. Quite often this missing abstraction is translated to a new class in your domain that represents some missing vocabulary. If the take the example illustrated in listing 1 we see that there is a HashMap of HashmMap. The key in the outer map is keyed on a persons last name and the inner collection is keyed on a persons first name.

public class AllPersons {

    private HashMap<String,HashMap> allPersons = new HashMap<String,HashMap>();

    public void addPerson(Person person) {
        HashMap<String,HashMap> persons = this.allPersons.get(person.getLastName());
        if (persons == null) {
            persons = new HashMap<String,Person>();
            this.allPersons.add(person.getLastName(), persons);
        persons.add(person.getFirstName(), person);

Listing 1. AllPersons, an implied collection.

The code smell in the example is that we are keying on the last name and then the first. The question is, what is the missing design element if there is one.

One of the roles that a collection can play is to implicitly create an index over a collection much in the same way we'd create an index in a database table. If we were to create an index on a single column, that would be a simple key. If we combine two or more simple keys to create another index, we have created a compound key. And this is exactly what we are doing in this example, creating an index based on two fields. From this we can conclude that the missing design element is a compound key. Listing 2. demonstrates the code that incorporates a class that implements our newly discovered abstraction.

public class AllPersons {

    private HashMap<CompoundKey,Person> allPersons = new HashMap<CompoundKey,Person>();

    public void addPerson(Person person) {
        this.addPerson(new CompoundKey(person.getFirstName(), person.getLastName(), person);

Listing 2. AllPersons, an implied collection using a CompoundKey.

If you found listing 2 to be more readable than the code in listing 1, then you've already experienced the biggest benefit, our added abstractions effect on readability. Another benefit is that the added abstraction often results in less code. Unfortunately this point isn't that clear when presented in a short example such as this. The benefits are realized only after repeated use of the abstraction. However the end results is that not only do we have less code to read, the code is more readable to begin with. In this case the abstraction also gives us a better memory utilization profile.

The next time you come across a collection or collections, think code smell. And then think, what is the missing abstraction. If you pay attention to these code smells, you'll quickly start to notice an improvement in your code base.

By Kirk Pepperdine

This work is licensed under a Creative Commons Attribution 3

Back to 97 Things Every Programmer Should Know home page

Personal tools