从字符串和整数创建哈希

12

我记得 Eclipse 和 IntelliJ IDEA 有自动生成对象 hashCode 的模板,根据其属性值生成。

如果使用一个数字和一个字符串作为属性值,则一种常见的策略如下:

  return stringValue.hashCode() + intValue * 32;

或类似于那样的东西。

我手头没有Eclipse或Idea,我想创建这样的功能。

编辑

根据答案,我创建了这个迷你类。

    class StringInt {
        private final String s;
        private final int i;

        static StringInt valueOf( String string , int value ) {
            return new StringInt( string, value );
        }
        private StringInt( String string, int value ) {
            this.s = string;
            this.i = value;
        }
        public boolean equals( Object o ) {
            if( o != null && o instanceof StringInt ){
                StringInt other = ( StringInt ) o;
                return this.s == other.s && this.i == other.i;
            }

            return false;
        }
        public int hashCode() {
            return s != null ? s.hashCode() * 37 + i : i;
        }
    }

这个类被用作一个大型内存映射的键( > 10k 元素)。我不想每次迭代查找字符串和整数是否相同。

谢谢。

PS.. 可能应该将其命名为StringIntKey。


Oscar,我认为这是一个很好的类。hashCode方法清晰、可靠且高效。那么如何防止字符串为空呢?在你的构造函数中,如果它为空,则抛出NPE。然后你可以在equals和hashCode中删除那些空值保护。最后,随时准备一本《Effective Java》以解决此类问题。Eclipse和IDEA创建的hashCode方法都基于该书。 - Steve McLeod
在您的equals方法中,应该使用equals而不是==来比较字符串。 - Steve Kuo
6个回答

8

HashCodeBuilder的源代码在线上吗?...我会去看一下。 - OscarRyz
字符串.hashCode * 37 + intValue!! 对我来说已经足够了!!谢谢 - OscarRyz

3

Eclipse总是执行大致相同的哈希函数,这里有一个示例,用于具有int和String字段的类:

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + this.interger;
        result = prime * result + ((this.string == null) ? 0 : this.string.hashCode());
        return result;
    }

他们总是选择31作为质数,然后乘以内置的哈希函数或值(如果它是原始值)。创建这样的方法并不难。
     public int hashCode(Object ... things) {
         final int prime = 31;
         int result = 1;
         for(Object thing : things) {
             result = prime * result + thing.hashCode();
         }
         return result;
     }

3

你也可以使用来自java.util.Objects包的Objects类快速获取哈希码。

@Override
public int hashCode() {
    return Objects.hash(this.string, this.integerValue, this.otherDataTypes);
}

2

或者,如果你不想添加另一个库,可以像下面这样做:

public int hashCode() {
    StringBuilder builder = new StringBuilder();
    builder.append(myString);
    builder.append(myInteger);
    return builder.toString().hashCode();
}

糟糕!有时候我会错过这种解决方案!!是的,我不想再添加另一个库。谢谢建议。 - OscarRyz
2
@aperkins:更简单的方法是直接写“return(myString + myInteger)。hashCode()”。Java编译器将把它编译成等效的StringBuilder.append调用序列。 - Stephen C
2
@perkins:另外一件事,如果你真的关心速度,那么这种方法比计算和组合组件哈希码要慢得多。 - Stephen C
2
hashCode 应该是快速的,最好使用字符串的 hashCode 并与整数进行简单的数学运算。 - Steve Kuo
速度问题可能是真的,但是我从来没有注意到在我们编写的代码中使用这种方法会有明显的减速。通常我们的性能问题更多地与电线传输或算法变化有关。 - aperkins

1

哈希码方法可能会被多次调用,因此值得优化。如果计算复杂,请考虑缓存哈希值。此外,避免做比必要计算更多的事情。(例如,StringBuilder解决方案大部分时间都在创建临时字符串。)

我想指出的另一件事是哈希的质量很重要。您要避免任何将许多常见键映射到同一个哈希码算法。如果发生这种情况,哈希表查找可能不再是O(1)。 (在最坏的情况下,它将是O(N)...即相当于线性搜索!)以下是一个糟糕的哈希函数示例:

int hashcode() {
    int hash = 1;
    for (int val : this.values) {
        hash = hash * value;
    }
    return hash;
}

考虑如果 this.values 中的一个元素为零会发生什么...


0

针对您最近的编辑,如果检索速度比存储问题更重要,您可以在构建StringInt类时预先计算并存储哈希码。这是安全的,因为您已经将Stringint字段标记为final,并且由于String是不可变的。

此外,您可以通过在进行完整比较之前检查要比较的对象是否== this来优化您的equals方法。我还建议先执行更便宜的基于int的比较,然后再比较字符串字段。

另一个最后的建议:您可以将valueOf(String,int)方法更改为构造StringInt或返回先前创建的实例(如果已存在具有相同Stringint值的实例)。这使得构建更加昂贵,但比较非常便宜,因为您可以使用“==”比较StringInt,知道没有两个StringInt将使用相同的Stringint值创建。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接