Что может вызвать сбой HashMap containsKey() с String в качестве ключа?

Я полностью смущен этим. Я запускаю полный пакет комплексных тестов. Вот соответствующий общий код, который используется рядом тестов JUnit:

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;
}

Вывод консоли из прогона JUnit включает следующие две последовательные записи:

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

Что мне не хватает? Почему сделал jaxbContexts.containsKey() не работает в этом случае для ключа на основе строки, в отличие от 46 других случаев во время выполнения JUnit? Мы не проводим наши тесты параллельно, но мы используем Аспекты, если это имеет значение.

5 ответов

Решение

Отладьте его и убедитесь, что класс, содержащий этот метод getJAXBContext(), создается только один раз (проверяя, имеет ли он одинаковый идентификатор памяти в режиме отладки для каждого вызова). Если это разные экземпляры, ключевое слово synchronized будет блокировать разные блокировки, и они будут использовать разные карты.

Лично я не стал бы беспокоиться с содержанием ключа.

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);
}

В Map нет ничего особенного, содержащего строки в качестве ключей. Просто замените println на new Exception().printStackTrace() и вы увидите, что происходит. Вы можете создавать два экземпляра класса, содержащего карту, или что-то еще.

За исключением гонки... Но вы говорите, что вы не запускаете вещи параллельно...

Во всяком случае, я бы позвонил context = jaxbContexts.get(clazz.getName()) и проверено context против null,

Ах, и использовал сам класс в качестве ключа, потому что несколько классов могут иметь одно и то же имя (например, загрузчики классов)

Карта может быть Map<Class, JAXBContext> вместо Map<String, JAXBContext> для более легкого использования.

Другие вопросы по тегам