странное поведение HashMap.put ()
Вопрос
Я пытаюсь устранить неполадку, которая связана с удалением объекта из HashMap и последующим возвращением этого же объекта с использованием нового ключа. Моя HashMap создается следующим образом:
transactions = new HashMap<Short, TransactionBase>();
Код, который выполняет переназначение, выглядит следующим образом:
transactions.remove(transaction.tran_no);
transaction.tran_no = generate_transaction_id();
transactions.put(transaction.tran_no, transaction);
Периодическое поведение, которое я наблюдаю, состоит в том, что код, который выполняется сразу после этого, который зависит от того, доступен ли объект транзакции, не находит объект транзакции с использованием нового идентификатора транзакции. Однако в какой-то момент в будущем транзакция может быть обнаружена. Итак, потянув за соломинку, есть ли какой-нибудь асинхронный эффект для put () или remove, который может вызвать такое поведение?
Я должен отметить, что, насколько мне известно, доступ к контейнеру осуществляется только одним потоком. Я уже читал в его документации, что класс HashMap не "синхронизирован".
Решение
Нет "асинхронного" эффекты в классе HashMap. Как только вы положили что-то туда, оно там. Вы должны проверить дважды и трижды, чтобы убедиться, что нет проблем с потоками.
Единственное, о чем я могу думать, это то, что вы где-то делаете копию HashMap. Очевидно, что на копию не повлияет добавление материала в оригинал или наоборот.
Другие советы
Существует небольшая разница между командами remove / get и put (хотя я предполагаю, что у вас есть проблема с многопоточностью).
Параметр для remove
/ get
имеет тип Object
; для put
он имеет тип K
. Причина этого была изложена много раз ранее. Это значит, что у него проблемы с боксом. Я даже не собираюсь угадывать, каковы правила. Если значение помещается в бокс в виде Byte
в одном месте и Short
в другом, эти два объекта не могут быть равными.
Существует похожая проблема с List.remove (int)
и List.remove (Object)
.
Я предполагаю, что каждый раз, когда вы проверяете наличие элемента, вы определенно используете short
или Short аргумент
для Map.get ()
или Map.contains ()
? Р>
Эти методы принимают аргументы объекта, поэтому, если вы передадите им int
, он будет преобразован в Integer
и никогда не будет соответствовать ни одному элементу вашей карты, поскольку все они будут иметь Короткие
клавиши.
Всего лишь одно предложение ... вы сосредотачиваетесь на доступе к HashMap, но мне интересно, стоит ли вам также посмотреть, является ли ваш generate_transaction_id () поточно-ориентированным или он ведет себя неожиданным образом.
Переопределили ли вы equals ()
, но не hashCode ()
в объектах модели? Как насчет compareTo ()
? Если вы ошибетесь, Коллекции будут вести себя странно.
Ознакомьтесь с рекомендациями по Java на equals () и compareTo () .
Что делает эта функция generate_transaction_id ()? Если он генерирует 16-битный GUID-подобный объект, вы можете легко получить хеш-коллизии. В сочетании с многопоточностью вы можете получить:
T1: transaction1.tran_no = generate_transaction_id(); => 1729
T2: transaction2.tran_no = generate_transaction_id(); => 1729
T1: transactions.put(transaction1.tran_no, transaction1); => map.put(1729, transaction1)
T2: transactions.put(transaction2.tran_no, transaction2); => map.put(1729, transaction2)
T1: int tran_no = transactions.get(1729); => transaction2
T1: transactions.remove(transaction.tran_no); => map.remove(1729)
T2: int tran_no = transactions.get(1729); => null
Конечно, это может быть решением, только если это «насколько вам известно» неверно.
Итак, вы знаете, что HashMap
не является поточно-ориентированным. Вы уверены, что к нему обращается только один поток? В конце концов, прерывистые сбои часто связаны с потоками. Если нет, вы можете заключить его в Collections.synchronizedMap ()
, примерно так:
Collections.synchronizedMap(transactions);
Вы всегда можете попробовать, чтобы исключить эту возможность.
Следует отметить, что это просто оборачивает исходную карту одним со всеми синхронизированными методами. Вы можете рассмотреть возможность использования синхронизированного блока, если доступ локализован. Р>
Потоки уже упоминались в нескольких ответах, но рассматривали ли вы проблему видимости для объектов, используемых несколькими потоками? Возможно (и довольно часто), что если вы поместите объект в коллекцию в одном потоке, он не будет "опубликован". в другие потоки, если вы не синхронизированы в коллекции должным образом.
Как уже видели другие, вы ДОЛЖНЫ знать, доступен ли HashMap только одним потоком или нет. CollectionSpy - это новый профилировщик, который позволяет мгновенно определить для всех контейнеров, сколько потоков выполняет какой-либо доступ. См. www.collectionspy.com для получения дополнительной информации.