Почему он не удаляется из набора?
Вопрос
Мне потребовалось некоторое время, чтобы найти эту ошибку...
Рассмотрим этот метод:
public void foo(Set<Object> set)
{
Object obj=set.iterator().next();
set.remove(obj)
}
Я вызываю метод с непустым набором хэшей, но ни один элемент не будет удален!
С чего бы это?
Решение
Для HashSet это может произойти, если хэш-код объекта изменяется после того, как он был добавлен в набор.Затем метод HashSet.remove() может заглянуть не в ту корзину хэшей и не найти ее.
Вероятно, этого бы не произошло, если бы вы выполнили iterator.remove() , но в любом случае хранение объектов в HashSet, хэш-код которого может измениться, - это ожидаемая случайность (как вы обнаружили).
Другие советы
Головоломка?Если Object.hashCode
, Object.equals
или "набор хэшей" был реализован неправильно (см., например, java.net.URL
- использовать URI
).
Кроме того, если набор (прямо или косвенно) содержит сам себя, скорее всего, произойдет что-то странное (именно то, что зависит от реализации и фазы луны).
Каков тип реализации набора и какие объекты находятся внутри набора?
- Если это HashSet, убедитесь, что значение метода hashCode() объекта остается постоянным между
set.put(...)
иset.remove(...)
. - Если это набор деревьев, убедитесь, что в объект не было внесено изменений, которые влияют на компаратор набора или
compareTo
способ.
В обоих случаях код между set.put(...)
и set.remove(...)
нарушает контракт, определенный реализацией соответствующего класса.Как правило, рекомендуется использовать неизменяемые объекты в качестве заданного содержимого (и в качестве ключей карты).По самой своей природе такие объекты не могут быть изменены, пока они хранятся внутри набора.
Если вы используете какую-то другую реализацию set, проверьте ее JavaDoc на предмет наличия контракта;но обычно либо equals
или hashCode
должен оставаться неизменным, пока объект содержится в наборе.
За пределами пропавшего ';' после set.remove(obj)
, Это может произойти в трех ситуациях (цитируется по javadoc).
ClassCastException - if the type of the specified element is incompatible with this set (optional). NullPointerException - if the specified element is null and this set does not support null elements (optional). UnsupportedOperationException - if the remove method is not supported by this set.
Вы также можете попробовать:
public void foo(Set<Object> set)
{
Object obj=set.iterator().next();
iterator.remove();
}
Должно ли это быть:
public void foo(Set<Object> set)
{
Iterator i = set.iterator();
i.next();
i.remove();
}
?
Ошибка может быть как-то связана с:
удалить публичную пустоту ()
Поведение итератора не определено, изменяется ли базовая коллекция во время выполнения итерации каким-либо образом иным, чем путем вызова этого метода.
(Ссылка)
Я не могу избавиться от ощущения, что (частично) проблема заключается в том, что набор передается по значению, а не по ссылке.Однако у меня не так уж много опыта в Java, так что я могу совершенно ошибаться.