Search code examples
design-patternsdictionaryhashmapanti-patterns

Pattern or antipattern: Using property of value as key in Dictionary/Hashmap


I wonder if it is good practice to use a property of the value as key in an Hashmap.

In Java it looks like:

private HashMap<String, VariableCondition> m_conditions = new HashMap<String, VariableCondition>();

// ...
m_conditions.put(var, new VariableCondition(var, value));

It would be critical if the HashMap is accessible from outside the object. But even as member variable you have to care about consistency of the HashMap yourself.

Is there an alternative pattern to realize the usecase. Using a List would be possible but you have to iterate the whole list for every access.


Solution

  • maybe there are prettier ways around but i use this quite a bit, especially during prototyping.

    collecting things:

    one example - very close to your example - is that you have a bunch of dates and you'd like to get lists for each day.

    List<Event> events = dearDataStorePlzGiveMeDates(); 
    TreeMap<Date, List<Event>> byDay = new TreeMap<>(); 
    for( Event e : events ){
        List<Event> forDay = byDay.get( e.getDate() ); 
        if( forDay == null ) byDay.put( forDay = new List<Event>() ); 
    
        forDay.add( e ); 
    }
    

    this is quite nice if you have a dull datastore and little amounts of data. i'm using treemaps instead of hashmaps because they have an ordering on the keys. i also vary the inner list type to some sorted data structure sometimes.

    note that this would be done quite differently in functional languages (map events to dates, sort, then collect events for each of those dates; very slow, but a 1-liner).

    as a dirty replacement for sets

    another thing if often need is make some data structure unique by a crazy criterion, while still keeping one instance of the associated data. consider this:

    List<Creatures> = ...; // from somewhere
    HashTable<Integer, Creature> examples = new HashTable<>(); 
    
    for( Create c : creatures ){
        examples.put( c.getNumberLegs(), c ); 
    }
    

    now this gets you an animal with one leg, an animal with two legs, etc.

    overal

    i don't mind these constructs at all, if you put a little comment about your intention on top they should be very easy to follow.

    i find them particularly useful if you have to work on existing data, i.e. you don't have access to the datastore (like mysql) or the datastore is just very dull and doesn't know how to accomplish such tasks.

    the big advantage of hashmaps is their complexity, they have O(1) access time (O(log n) for treemaps) which is quite good.

    alternatives

    it sounds like what you really want is an "index", a way to quickly access instances based on one of their properties. in java there's the TreeSet class for that. It has O(log n) complexity for all operations and is used something like this:

    TreeSet<MyClass> indexed = new TreeSet<>( new Comparator<MyClass>(){
        public int compare( MyClass a, MyClass b ){
            // "sort" by the property (assuming they are Integers)
            return a.getThing().compareTo( b.getThing() ); 
        }
    }); 
    
    indexed.addAll( theList ); 
    

    attention though: (1) the behaviour here differs from your example!! assume keys appear multiple times, in your code (with hashtable) the last duplicate would survive. in this case the first element would survive (the treemap basically says "this is already in there, so ...whatever") (2) runtime complexity is good but way worse than the O(1) runtime when using HashMaps.

    hope this answers your question ...