Java 如何有效地删除更新的HashSet项
鉴于下面的代码片段,我们如何有效地删除以前更新/更改过的元素Java 如何有效地删除更新的HashSet项,java,hashset,Java,Hashset,鉴于下面的代码片段,我们如何有效地删除以前更新/更改过的元素 public static class Foo { @Override public int hashCode() { return new Random().nextInt(); } } public static void main(String[] args) { Set<Foo> set = new HashSet<>(); set.add(n
public static class Foo {
@Override
public int hashCode() {
return new Random().nextInt();
}
}
public static void main(String[] args) {
Set<Foo> set = new HashSet<>();
set.add(new Foo());
set.removeIf(f -> true); // Returns true, but no deletion occurs
assert set.size() == 0; // Fails as set still contains it's single item
}
根本没有随机散列
确实存在不同的散列,但显然,如果元素曾经被更改过(因此,它们的散列),那么它们永远无法被删除,不管发生什么。我们可以在两个
Set::remove
调用中确认,其中Set.remove(oldBar)
应该删除元素,因为oldBar
散列等于添加bar
时的散列。这里的问题是,如果修改对象的方式使散列代码不同,那么它在结构上就不再是同一个对象。另一种说法是,original.equals(modified)
是false
(或者至少应该归因于equals()
和hashCode()
的契约。一种解决方案是修改hashCode()
基于某个不变量进行计算。换句话说,返回的哈希代码仅基于Foo
对象中的标识数据,无论发生什么都不会更改。例如,这可能是一个id
,例如映射到基础数据库表的对象
或者,您可以找到与您的用例更匹配的不同数据结构。例如,ArrayList
可能更合适,因为您可以删除给定索引中关于该对象状态的项。与所有其他答案和注释一样,首先,我应该说hashCode
应该保持不变sistent:当元素存储在基于散列的集合中时,它应该保持不变
有趣的是,当查询集合的
大小时,OpenJDK 11中的代码片段将返回0
,但在OpenJDK 8上它将保持为1
发生这种情况的原因是标准库中的更改(请参阅):
HashMap#keySet
中的removeIf
(HashSet
使用下方的HashMap
)未被覆盖,因此它依赖于迭代器#remove
- 后一种方法的实现已经更改,以避免在
HashMap.hash迭代器::remove
方法中重新计算hashCode
- 因此
removeIf
将成功删除元素
- 请参阅提交:
-K key=p.key;
-removeNode(散列(键)、键、null、false、false);
+removeNode(p.hash,p.key,null,false,false);
再次声明:不要依赖此实现细节不一致的哈希代码是错误的
但是,我无法理解在您调用removeIf时,它会对您的情况产生怎样的影响,removeIf会迭代集合中的所有元素
所以我用JAVA 11尝试了一下,它成功了。集合被清空,返回的大小如预期的那样是0。我很好奇你们使用了什么配置
public static void main(String[] args){
Set<Foo> s = new HashSet<>();
s.add(new Foo("user1", 3));
s.add(new Foo("user2", 5));
s.forEach( e -> System.out.println(e));
s.removeIf(f-> true);
s.forEach( e ->System.out.println(e));
System.out.println(s.size());
}
publicstaticvoidmain(字符串[]args){
Set s=新的HashSet();
s、 添加(新的Foo(“user1”,3));
s、 添加(新的Foo(“user2”,5));
s、 forEach(e->System.out.println(e));
s、 removeIf(f->true);
s、 forEach(e->System.out.println(e));
System.out.println(s.size());
}
hashCode()背后的合同说,在多次调用时,该值必须返回相同的值,因此随机值不是一个好的选择。@在帖子中所说的MAnouti,多次调用<代码>对象::hash码< /COD>导致对象属性是否改变了不同的值。另一个具有任何非null值。将新的非null值分配给以前的null属性,这将导致不同的哈希值。@Henri As,这正是取消对象放入哈希集中的资格的行为。
@BasilBourque您的观点是什么?如果一个元素被添加到一个集合中,并且以后被更改,你是说它将永远存在吗?显然,即使在添加元素时使用相同的哈希值,也无法删除该元素。我在我的帖子中添加了一个不同的示例。@亨利,你真的没有阅读这里发布的Javadoc或该文档的引用吗?该文档清楚地表明,你不能通过对象存储在HashSet
中的集合。你的一厢情愿并不会改变这一点。也许你想要一个集合来观察它所包含的对象,以改变它的哈希代码值。HashSet
不是这样的集合。要对一个会改变它的哈希代码值的对象进行更改,首先将它从se中删除t、 对对象进行变异,然后将对象重新提交给集合。woaa刚刚粘贴了相同的东西。很明显,这是Denis所说的一个算法问题,从Java 11开始已经解决了。Saldy我使用Java 8(无法更新)。集合必须使用。此外,我更新了帖子来解释“随机散列”部分
public static void main(String[] args){
Set<Foo> s = new HashSet<>();
s.add(new Foo("user1", 3));
s.add(new Foo("user2", 5));
s.forEach( e -> System.out.println(e));
s.removeIf(f-> true);
s.forEach( e ->System.out.println(e));
System.out.println(s.size());
}