【发布时间】:2012-01-16 23:13:16
【问题描述】:
也许以前有人问过这个问题(我没找到)...
我有一个大约 java.util.Set。 50000 个字符串。我想生成某种哈希来检查它是否已更改(比较两个版本的 Set 的哈希)?
如果 Set 发生变化,则哈希必须不同。
如何实现?谢谢!
编辑:
很抱歉这种误导性的措辞。我不想检查“它”是否已更改(同一个实例)。相反,我想检查两个数据库查询,它们生成两个 - 可能相同 - 一组字符串的实例是否相等。
【问题讨论】:
也许以前有人问过这个问题(我没找到)...
我有一个大约 java.util.Set。 50000 个字符串。我想生成某种哈希来检查它是否已更改(比较两个版本的 Set 的哈希)?
如果 Set 发生变化,则哈希必须不同。
如何实现?谢谢!
编辑:
很抱歉这种误导性的措辞。我不想检查“它”是否已更改(同一个实例)。相反,我想检查两个数据库查询,它们生成两个 - 可能相同 - 一组字符串的实例是否相等。
【问题讨论】:
我会尝试使用java.util.AbstractSet 的hashCode 方法,如文档中所述:
返回此集合的哈希码值。一个集合的哈希码是 定义为集合中元素的哈希码之和, 其中空元素的哈希码定义为零。这个 确保 s1.equals(s2) 意味着 s1.hashCode()==s2.hashCode() 对于任何两组 s1 和 s2,根据总合同的要求 Object.hashCode().
当然,这仅在您的 Set 实现从 AbstractSet 扩展时才有效,我想您使用例如java.util.HashSet。 与往常一样,存在哈希冲突的可能性。
或者,您可以扩展现有的 Set 实现并覆盖状态更改方法,如果每个对象的哈希计算变得过于昂贵,这可能是有意义的,例如:
class ChangeSet<E> extends java.util.HashSet<E> {
private boolean changed = false;
@Override
public boolean add(E e) {
changed = true;
super.add(e);
}
public void commit() {
changed = false;
}
public boolean isChanged() {
return changed;
}
/* and all the other methods (addAll, remove, removeAll, etc.) */
}
【讨论】:
基于此声明:
If the Set changes, the hash has to be different
这真的无法实现,除非你有更多的限制。通常,哈希是某个固定空间中的值。例如,您的哈希可能是一个 32 位整数,因此有 2^32 个可能的哈希值。一般来说,b 位可以获得 2^b 个可能的哈希值。为了实现您想要的,您必须确保每个可能的集合(即 - 所有集合的集合!)小于或等于 2^b。但我的猜测是你可以有任意字符串,所以这是不可能的。即使有可能,您也必须想出一种映射到哈希空间的方法,这可能具有挑战性。
但是,使用良好的散列函数,更改集合最终产生相同散列值的可能性不大。所以可以使用hash来判断不等,但是如果hash相同,还是需要检查是否相等。 (这与哈希集或哈希映射背后的想法相同,其中元素根据哈希码映射到存储桶,但您必须检查是否相等)。
与 Paul 提到的类似但不同:您可以改为创建具有版本号的集合实现,并确保在集合发生变异时始终生成新的版本号。那你可以比较一下版本号吗?我不确定您是否关心不可变集或可变集是否更改回您所见过的版本(即 - 如果它应该始终获得相同的版本)。
希望这会有所帮助。
【讨论】:
如果您需要提高 hashCode 的性能(因为它对于大型 Set 来说相当昂贵),您可以缓存它并随时更新。
class MyHashSet<E> extends LinkedHashSet<E> {
int hashCode = 0;
@Override
public boolean add(E e) {
if (super.add(e)) {
hashCode ^= e.hashCode();
return true;
}
return false;
}
@Override
public boolean remove(Object o) {
if(super.remove(o)) {
hashCode ^= o.hashCode();
return true;
}
return false;
}
@Override
public void clear() {
super.clear();
hashCode = 0;
}
@Override
public int hashCode() {
return hashCode;
}
}
【讨论】:
+ 和 - 应该是相同的,即使有上溢和下溢,但^ 更容易看出它是如何工作的。
有时越简单越好。我建议编写自己的 Set 实现。在其中,覆盖 add 和 remove 方法,以便在修改 Set 时设置一个标志。为标志添加一个 getter,isModified,您不必担心哈希开销或冲突。只需致电MyCustomSet.isModified。
或者,您可以调用 Collections.unmodifiableSet 以获取无法修改的 Set 的包装。如果代码尝试修改集合,将引发异常。
【讨论】: