【问题标题】:Cumulative string collection hashing累积字符串集合散列
【发布时间】:2011-08-17 06:38:37
【问题描述】:

是否有适用于 Java 的算法,允许我不断添加 String 对象并删除旧对象,这样如果我添加 String 然后稍后将其删除,整数散列将是相同的?

编辑:哈希中的字符串是唯一的。

一些伪代码:

h = hash
add(h, "hi!") == 51;
add(h, "hello again!") == 532;
rem(h, "hello again!") == 51;

我知道您可以使用 Java 集合来做到这一点,但默认实现必须继续遍历整个集合以收集哈希码。这对于大型集合来说确实是低效的。如果存在,我不介意使用外部库。

提前致谢,
克里斯

【问题讨论】:

    标签: java collections performance hash


    【解决方案1】:

    如果您不关心哈希算法是否具有密码质量(密码哈希算法很难正确指定;您搞砸了,当您不希望他们这样做时,有人可能会导致冲突),以下应该工作:

    考虑以下代码:

    interface Accumulator<T, U>
    {
        public void add(T t);
        public void subtract(T t);
        public U get();
    }
    
    class SumHasher implements Accumulator<String,Integer>
    {
        @Override private int accumulator = 0;
        @Override public void add(String t) { accumulator += t.hashCode(); }
        @Override public void subtract(String t) { accumulator -= t.hashCode(); }
        @Override public Integer get() { return accumulator; }
    }
    
    class XorHasher implements Accumulator<String,Integer>
    {
        @Override private int accumulator = 0;
        @Override public void add(String t) { accumulator ^= t.hashCode(); }
        @Override public void subtract(String t) { accumulator ^= t.hashCode(); }
        @Override public Integer get() { return accumulator; }
    }
    

    它们的共同点是加法和异或都是关联并具有的操作。您可以按任何顺序执行它们并按任何顺序撤消它们,因此如果您对Set&lt;T&gt; 中的每个元素add(),然后对集合中的每个元素subtract()(不一定以相同的顺序),您保证得到0。

    当然还有其他操作可以满足此属性,但我不确定它们是什么。 (乘法将不起作用,除非您可以保证累积的所有项目的值都不为 0。这个答案曾经使用 f(x,h) = ((x^h) + h)^h 和 g(x,h ) = ((x^h) - h)^h 作为逆函数,但这些函数不是关联的:以不同顺序累加元素会产生不同的结果。

    编辑确实想到了另一个简单的方法:基于输入值的按位排列(其中按位旋转是一种特殊情况)。在 Java 中,您可以使用 (x &lt;&lt; k) | (x &gt;&gt;&gt; (32-k)) 实现按位旋转,其中 x 是整数,k 是 0 到 31 之间的整数(例如,从另一个数字中取任意 5 位)。 &gt;&gt;&gt; 不是错字:您需要使用它,因为常规的 &gt;&gt; 会进行符号扩展。 糟糕,只有在集合中的元素被删除时才有效倒序。

    编辑 2:最后,您可以更一般地实现此方法,如下所示:

    abstract class AbstractHashCodeAccumulator<T> implements Accumulator<T, Integer>
    {
        private int accumulator = 0;
        abstract protected int combine(int a, int h);
        abstract protected int uncombine(int a, int h);
        @Override public void add(T t) { accumulator = combine(accumulator, t.hashCode());
        @Override public void subtract(T t) { accumulator = uncombine(accumulator, t.hashCode());
        @Override public Integer get() { return accumulator; }
    }
    
    class SumHasher extends AbstractHashCodeAccumulator<String>
    {
        @Override protected int combine(int a, int h)   { return a+h; }
        @Override protected int uncombine(int a, int h) { return a-h; }
    }
    
    class XorHasher extends AbstractHashCodeAccumulator<String>
    {
        @Override protected int combine(int a, int h)   { return a^h; }
        @Override protected int uncombine(int a, int h) { return a^h; }
    }
    

    这种方法的问题在于,在某些方面它是“非散列式的”,即它需要有序,而散列通常需要无序/熵/不可逆性。

    【讨论】:

    • 这很好。仔细想想,这段代码是有道理的。你认为发生冲突的概率是多少?我还可以使用集合的大小来进一步减少冲突。
    • 哦,顺便说一句,你在 xor 哈希器上的 add 和 remove 是一样的。这是故意的吗?
    • @Chris:xor 是它自己的逆,所以是的,它是有意的。
    • @Jason 这是一个非常聪明的方法。我喜欢! Accumlator 在什么包中?
    • @glowcoder: Accumulator 不在包中,但您可以对大多数包含函数式内容的库执行非常相似的操作; MapReduce 中的Reduce 操作和列表折叠(en.wikipedia.org/wiki/Fold_%28higher-order_function%29)都是广义累加器。或者就此而言,只需像上面那样制作自己的 Accumulator 接口。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    • 2015-10-31
    • 2016-05-06
    • 2011-09-30
    • 2020-03-17
    相关资源
    最近更新 更多