我在 HashSet 上调用 Iterator.remove() 时遇到问题。
我有一组带有时间戳的对象。在将新项目添加到集合之前,我会遍历集合,识别该数据对象的旧版本并将其删除(在添加新对象之前)。时间戳包含在 hashCode 和 equals() 中,但不包含在 equalsData() 中。
for (Iterator i = allResults.iterator(); i.hasNext();)
{
DataResult oldData = i.next();
if (data.equalsData(oldData))
{
i.remove();
phá vỡ;
}
}
allResults.add(data)
奇怪的是,对于集合中的某些项目, i.remove() 静默失败(没有异常(exception))。我已经验证了
实际上调用了 i.remove() 行。我可以直接在Eclipse的断点处从调试器调用它,它仍然无法改变Set的状态
DataResult 是一个不可变的对象,所以它在最初添加到集合后不能改变。
equals 和 hashCode() 方法使用 @Override 来确保它们是正确的方法。单元测试验证这些工作。
如果我只使用 for 语句和 Set.remove 代替,这也会失败。 (例如,遍历项目,在列表中找到项目,然后在循环后调用 Set.remove(oldData))。
我在 JDK 5 和 JDK 6 中测试过。
我认为我一定错过了一些基本的东西,但是在我和我的同事花了一些时间在这之后,我感到很困惑。有什么要检查的建议吗?
biên tập:
有一些问题 - DataResult 真的是不可变的。是的。没有二传手。并且当检索到 Date 对象(它是一个可变对象)时,它是通过创建一个副本来完成的。
public Date getEntryTime()
{
return DateUtil.copyDate(entryTime);
}
public static Date copyDate(Date date)
{
return (date == null) ? null : new Date(date.getTime());
}
进一步编辑(稍后):记录一下——DataResult 不是一成不变的!它引用了一个对象,该对象的哈希码在持久保存到数据库时会发生变化(我知道这是不好的做法)。事实证明,如果使用临时子对象创建 DataResult,并且子对象被持久化,则 DataResult 哈希码已更改。
非常微妙——我看了很多遍,并没有注意到缺乏不变性。
我还是对这个很好奇,写了如下测试:
import java.util.HashSet;
nhập java.util.Iterator;
import java.util.Random;
import java.util.Set;
public class HashCodeTest {
private int hashCode = 0;
@Override public int hashCode() {
return hashCode ++;
}
public static void main(String[] args) {
Set set = new HashSet();
set.add(new HashCodeTest());
System.out.println(set.size());
for (Iterator iter = set.iterator();
iter.hasNext();) {
iter.next();
iter.remove();
}
System.out.println(set.size());
}
}
导致:
1
1
如果一个对象的 hashCode() 值在添加到 HashSet 后发生了变化,它似乎使该对象不可移除。
我不确定这是否是您遇到的问题,但如果您决定重新访问,则需要考虑一下。
Tôi là một lập trình viên xuất sắc, rất giỏi!