Chris Knight
Chris Knight

Reputation: 25074

What can cause HashMap containsKey() to fail with String as the key?

I'm thoroughly confused by this one. I'm running a full package worth of unit tests. Here's the relevant shared code which gets used by a number of JUnit tests:

private static Map<String, JAXBContext> jaxbContexts = 
                             new HashMap<String, JAXBContext>();

private synchronized JAXBContext getJAXBContext(Class clazz) throws JAXBException {
    JAXBContext context = null;
    if (jaxbContexts.containsKey(clazz.getName())) {
        context = jaxbContexts.get(clazz.getName());
    } else {
        context = JAXBContext.newInstance(clazz);
        System.out.println("Created new context for '" + clazz.getName() + "'");
        jaxbContexts.put(clazz.getName(), context);
    }
    return context;
}

The console output from the JUnit run includes the following two consecutive entries:

Created new context for 'com.somecompany.xmlschema.providepensionpaymentinfo.Interface'
Created new context for 'com.somecompany.xmlschema.providepensionpaymentinfo.Interface'

What am I missing? Why did jaxbContexts.containsKey() not work in this instance for a String based key, unlike 46 other times during the JUnit execution? We aren't running our tests in parallel, but we do use Aspects if that makes a difference.

Upvotes: 2

Views: 3768

Answers (5)

卢声远 Shengyuan Lu
卢声远 Shengyuan Lu

Reputation: 32014

The map can be Map<Class, JAXBContext> instead of Map<String, JAXBContext> for easier usage.

Upvotes: -1

maaartinus
maaartinus

Reputation: 46482

There's surely nothing special about Map containing strings as keys. Just replace the println by new Exception().printStackTrace() and you'll see what's going on. You may be creating two instances of the class holding the map, or whatever.

Upvotes: 1

cadrian
cadrian

Reputation: 7376

Except for a race... But you say that you don't run things in parallel...

Anyway, I would have called context = jaxbContexts.get(clazz.getName()) and tested context against null.

Ah, and used the class itself as a key, because more than one class may have the same name (think class loaders)

Upvotes: 0

Geoffrey De Smet
Geoffrey De Smet

Reputation: 27322

Debug it and verify that the class that contains this getJAXBContext() method is instantiated only once (by checking it's has the same memory id in debug mode for every call to it). If it's different instantiations, the synchronized keyword will lock on different locks and they will use different maps.

Upvotes: 3

Paul Tomblin
Paul Tomblin

Reputation: 182822

Personally, I wouldn't bother with containsKey.

String name = clazz.getName();
context = jaxbContexts.get(name);
if (context == null) {
    context = JAXBContext.newInstance(clazz);
    System.out.println("Created new context for '" + name + "'");
    jaxbContexts.put(name, context);
}

Upvotes: 1

Related Questions