Вопрос

Мне потребовалось некоторое время, чтобы найти эту ошибку...

Рассмотрим этот метод:

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, так что я могу совершенно ошибаться.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top