1

Let's say I have a simple Java object, let's call it DefinedData. It will contain a number of final fields of varying types, such as strings, integers, enums, and even perhaps a set or two of strings. All in all, it's just a relatively simple data container. There will be potentially 1k to 2k of these, all static final objects. Most of these fields will be unique in that no other DefinedData object will have the same value for that field.

These will be placed into a Map of (DefinedData, Object). Now, you could easily get that Object out of the Map if you have the DefinedData object, but what if you only have one of the unique field values? You can't just pass that to the Map. You'd have to iterate over the keys and check, and that would mean wrapping the map with a lookup method for each field in DefinedData. Doable, but not the prettiest thing out there, especially if there are a lot of values in the Map and a lot of lookups, which is possible. Either that or there would need to be a lookup for DefinedData objects, which would again be a bunch of Maps...

This almost sounds like a job for a database (look up based on any column), but that's not a good solution for this particular problem. I'd also rather avoid having a dozen different Maps, each mapping a single field from DefinedData to the Object. The multikey maps I've seen wouldn't be applicable as they require all key values, not just one. Is there a Map, Collections, or other implementation that can handle this particular problem?

user1017413
  • 2,023
  • 4
  • 26
  • 41
  • Multiple maps is going to be the only solution here. Why are you averse to doing that? You could write a `MultiKeyMap` wrapper to hide the complexity from client code. – Jim Garrison Nov 18 '13 at 22:04
  • Eh, maybe I'm just being a bit anal about the whole thing. It's not a big deal, but I was hoping there would be something a bit cleaner. Yes, the complexity can be hidden, but I was hoping to find something that could better encapsulate the solution. Building N Hashmaps feels a bit bulky. – user1017413 Nov 18 '13 at 22:11
  • You have to provide a lookup function using N different key sets. You won't find anything simpler than N hashmaps. – Jim Garrison Nov 18 '13 at 22:12

2 Answers2

0

The only way to avoid having multiple maps is by iterating through all your DefinedData objects in some way. Reason being, you have no way of knowing how to divide them out or sort them until the request is made.

An example could be made if you had a bucket of apples. At any moment someone may come up and request a certain color, a certain kind, or a certain size. You have to choose to sort by one of those categories, and the other categories have to be searched through all the apples. If only you could have three identical sets of apples; one for each category.

Having multiple maps would be a faster solution, though take up more memory, while iterating would be easier to achieve, slower, and use less memory.

DoubleDouble
  • 1,493
  • 10
  • 25
0

I hesitate to propose this, but you could encapsulate your lookups behind some sort of Indexer class that auto-generates a single map via reflection using the fields of supplied objects.

By single map, I mean just one single map for the whole indexer which creates a key based on both the field name and data (say concatenating the string representing the field name with a string representation of the data).

Lookups against the indexer would supply both a field name and data value, which would then be looked up in the single map encapsulated by the indexer.

I do not think this necessarily has any advantage over a similar solution where the indexer is instead backed by a map of maps (map of field name to map of data to object).

The indexer could also be designed to use annotations so that not all fields are indexed, only those suitably annotated (or vice-versa, with annotations to exclude fields).

Overall, a map of map solutions strikes me as easier since it cuts out the step of complicated key assembly (which could be complicated for certain field data types). In either case, encapsulating it all in an Indexer that auto-generates its maps seems to be the way to go.

Update:

Made a quick non-generified proof of concept for an Indexer type class (using the map of maps approach). This is in no way a finished work, but illustrates the concept above. One major deficiency being the reliance on beans, so both public and private fields without accessor methods are invisible to this indexer.

public class Indexer
{
    private Map<String,Map<Object,Set<Object>>> index = new HashMap<String,Map<Object,Set<Object>>>();

    // Add an object to the index, all properties are indexed.
    public void add(Object object) throws Exception
    {
        BeanInfo info = Introspector.getBeanInfo(object.getClass());

        PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
        for (PropertyDescriptor descriptor : propertyDescriptors)
        {
            String fieldName = descriptor.getName();
            Map<Object,Set<Object>> map = index.get(fieldName);
            if (map == null)
            {
                map = new HashMap<Object,Set<Object>>();
                index.put(fieldName, map);
            }
            Method method = descriptor.getReadMethod();
            Object data = method.invoke(object);
            Set<Object> set = map.get(data);
            if (set == null)
            {
                set = new HashSet<Object>();
                map.put(data, set);
            }
            set.add(object);
        }

    }

    // Retrieve the set of all objects from the index whose property matches the supplied.
    public Set<Object> get(String fieldName, Object value)
    {
        Map<Object,Set<Object>> map = index.get(fieldName);
        if (map != null)
        {
            Set<Object> set = map.get(value);
            if (set != null)
            {
                return Collections.unmodifiableSet(set);
            }
        }

        return null;
    }
}
Trevor Freeman
  • 7,112
  • 2
  • 21
  • 40