【问题标题】:Best way to hash sets of three integers散列三个整数的最佳方法
【发布时间】:2013-12-12 08:34:46
【问题描述】:

我正在尝试编写一个程序,该程序可以快速找到与带有通配符和已知字母的字符串匹配的所有单词。例如,L*G 将返回 LOG、LAG、LEG。我正在寻找可以让我快速查找的东西,但我并不关心首先创建树所需的时间。

我的想法是“Triples”映射到字符串 ArrayList 的 Hashmap:基本上,所有字符串的列表都符合某个索引、该索引处的字符和长度的标准。

但我现在的问题是为这些“三元组”生成一个好的哈希函数,这样每个三元组都是唯一的。

这是我现在拥有的代码。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class CWSolutionT {
HashMap<Triple, ArrayList<String>> tripleMap = new HashMap<Triple, ArrayList<String>>();

public CWSolutionT(List<String> allWords) {
    for (String word : allWords) {
        for (int letter = 0; letter < word.length(); letter++) {
            ArrayList<String> tempList = new ArrayList<String>();
            Triple key = new Triple(letter, word.charAt(letter),
                    word.length());
            if (tripleMap.get(key) == null) {
                tempList.add(word);
                tripleMap.put(key, tempList);
            } else {
                tempList = tripleMap.get(key);
                tempList.add(word);
                tripleMap.put(key, tempList);
            }
        }
    }
}

public List<String> solutions(String pattern, int maxRequired) {
    List<String> sol = new ArrayList<String>();
    List<List<String>> solList = new ArrayList<List<String>>();
    int length = pattern.length();
    for (int i = 0; i < length; i++) {
        if (pattern.charAt(i) != '*') {
            Triple key = new Triple(i, pattern.charAt(i), pattern.length());
            solList.add(tripleMap.get(key));
        }
    }
    if (solList.size() == 0) {
        // implement later
    }

    if (solList.size() == 1)
        return solList.get(0);

    for (List<String> list : solList) {
        sol.retainAll(list);
    }
    return sol;
}

private class Triple {
    public final int index;
    public final char letter;
    public final int length;

    public Triple(int ind, char let, int len) {
        index = ind;
        letter = let;
        length = len;
    }

    public boolean equals(Object o) {
        if (o == null)
            return false;
        if (o == this)
            return true;
        if (!(o instanceof Triple)) {
            return false;
        }
        Triple comp = (Triple) o;
        if (this.hashCode() == comp.hashCode())
            return true;
        return false;
    }

    public String toString() {
        return "(" + index + ", " + letter + ", " + length + ")";
    }

    public int hashCode() {
        return (int) (.5 * (index + letter + length)
                * (index + letter + length + 1) + letter + length);
    }
}
}

【问题讨论】:

  • 请建立SSCCE 来演示问题
  • hashCode不能这样用,看我的回答。
  • 如果你正在写一个文字游戏,你应该查找有向无环词图。

标签: java optimization data-structures hashmap tuples


【解决方案1】:

您还必须在 Tuple 类中覆盖 hashCode()

【讨论】:

  • 因为您的元组正在处理对象,所以使用它们的 hashCodes 和 cantor 配对从这些对象中计算出唯一的 hashCode:en.wikipedia.org/wiki/…
  • @Kai Huppmann:只要发生溢出,Cantor 配对就会停止工作。实际上,对于有限的域,没有理由尝试。
  • @maartinus 不明白你的意思。当然,对于大的原始哈希码,您将超出整数范围,但 hashCode() 和 equals() 的合同仍将被履行。 “对于有限的域,没有理由尝试”是什么意思?你的选择是什么?
【解决方案2】:

HashMap 需要equalshashCode 的实现,并且您还没有覆盖它们中的任何一个。现在您的地图基本上只检查对象识别 (someOldObject == someNewObject)。

看起来您尝试实现 equals 方法但签名错误,Object#equalsObject 作为参数,而您的则采用 Tuple,因此它被重载。这个问题的答案有一些实现equals和hashCode的技巧:What issues should be considered when overriding equals and hashCode in Java?

【讨论】:

    【解决方案3】:

    一般来说,将两个ints 压缩成一个是不可能的。康托尔配对函数适用于无限域,但将其转移到ints 无济于事。

    您的输入是一个三元组(int, char, int),理论上说明了2**32 * 2**16 * 2**32 = 2**80 的可能性,这显然不能单射地映射到仅包含2**32 元素的集合。

    借助有关输入值的一些知识,您可以做得更好,但可能还不够好。例如,您知道indexlength 都是非负数,这会使您的域缩小四倍……但这没什么。

    如果你知道indexlength 都小于256,你可以通过

    public int hashCode() {
        return index + 256 * length + 256 * 256 * letter;
    }
    

    这个表达式可以更有效地写成

    index + (length << 8) + (letter << 16)
    

    但您可以将其留给 JIT 编译器。

    您很可能无法充分减少域,然后我建议您顺其自然。只需比较字段...您可以尝试将其放在很长的位置,但这不就是the root of all evil吗?

    请注意您的

    public int hashCode() {
        return (int) (.5 * (index + letter + length)
                * (index + letter + length + 1) + letter + length);
    }
    

    太糟糕了....您正在使用 三件套!元组(0, 'A', 1)(0, 'B', 0) 具有相同的hashCode,因此您的equals 返回true。你实际上是康托尔配对 indexletter + length 而你必须使用两个配对。

    【讨论】:

      猜你喜欢
      • 2021-05-21
      • 1970-01-01
      • 2016-11-07
      • 1970-01-01
      • 2010-11-24
      • 1970-01-01
      • 1970-01-01
      • 2012-01-20
      • 1970-01-01
      相关资源
      最近更新 更多