【问题标题】:Better hashCode function for stock ticker symbols?股票代码符号的更好的 hashCode 函数?
【发布时间】:2016-04-08 17:53:46
【问题描述】:

我将股票对象保存在 HashMap 中,其中键是股票代码 String(例如,"AAPL" 代表 Apple, Inc.)。不幸的是,这是不可行的,因为 Ally Financial Inc (GM1) 和 Global Partners LP (GLP) 的哈希码相互冲突,并且会相互覆盖。例如:"GM1".hashCode() == "GLP".hashCode() == 主要问题。

股票代码字符串是否有hashCode 可以保证不发生冲突?

public Class StockTicker {
    public String symbol;

    public StockTicker(String symbol) { this.symbol = symbol; }

    @Override
    public int hashCode() {
        // What goes here?
    }
}

成功的答案可能会利用代码字符串不超过 5 个字符,并且是大写字母数字(“.”除外)这一事实。如“BRK.B”。

【问题讨论】:

  • 如果您覆盖哈希码,请确保您也覆盖 equals。
  • 可以编写一个完美的(对不相等的对象没有冲突)哈希码,但是您知道 HashMap 将正确处理冲突(尽管性能受到非常轻微的影响),即使在发生冲突时也是如此,是吗?不只使用return symbol.hashCode(); 就够糟糕了吗?
  • @Mshnik 你的意思是不可能吗?
  • @christopher,是的。 Equals 是微不足道的,所以想把问题集中在 hashCode 实现上。
  • 请注意,除非您的收藏有 2^32 个桶,否则您的 hashCode 将减少。例如假设你只有 1024 个桶,那么 hashCode 将减少到只有 10 位,即使使用唯一的 hashCode,你也会遇到冲突。

标签: java hash hashmap hashcode


【解决方案1】:

我不认为键字符串的 hashCode 对地图本身有任何影响(我假设您使用实际的股票代码字符串作为键,而不是哈希码;如果您使用哈希插入地图代码,那么是的,这会导致问题)。我进行了快速测试,运行良好。

private Map<String, String> stockMap = new HashMap<String, String>();

@Test
public void mapTest() {
    stockMap.put("GM1", "gm1stock");
    stockMap.put("GLP", "glpstock");

    assertEquals(2, stockMap.size());
}

就像 Mshnik 所说,Java 会为您处理冲突,因此您无需担心。您能否详细说明具体是什么代码导致了您的问题?

【讨论】:

    【解决方案2】:

    正如其他答案和 cmets 所指出的那样,A) Java 将正确处理冲突,假设您已经以一种令人满意的方式编写了 equals 和 hashcode,并且 B) 即使获得了完美的 hashcode 函数也不能保证您不会得到碰撞。

    话虽如此,可以为您的规范编写一个完美的哈希码函数。正好有 37 个字符需要担心(26 个字母、10 个数字和.),小于 64。因此我们可以使用 6 位来表示每个字符。您最多有 5 个字符,这意味着您的哈希码最多占用 30 位,这适合 int。

    这是一个创建完美哈希码的实现:

      public static class Stock{
        // The possible characters of a stock - note length is < 64
        private final static String alphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.";
    
        //Will be 6 for given valid chars, but coding it like this prevents bugs later
        private final static int shiftAmnt = (int)(Math.log(alphaNumeric.length()) / Math.log(2)) + 1;
    
        private String stock;
    
        public Stock(String s) {
          stock = s;
        }
    
        @Override
        public boolean equals(Object o) {
          return o instanceof Stock && stock.equals( ((Stock)o).stock);
        }
    
        @Override
        public int hashCode() {
          int code = 0;
          for (char c : stock.toCharArray()) {
            code = code << shiftAmnt;
            code += alphaNumeric.indexOf(c);
          }
          return code;
        }
      }
    

    【讨论】:

    • 很好,但是因为哈希码的目的是获取一个“大”对象并将其缩减为一个小的唯一标识符,所以尝试对一个由五个字符组成的字符串进行哈希处理有什么意义吗?如果一个 Java 字符大约是 2 个字节,而不是 5 个字符是 80 位(大约),那么散列在理论上似乎完全没有必要。
    • (我知道您提供此实现只是作为示例,不一定提倡使用它。您只是在考虑哈希码及其用途,所以我问了上述问题。)
    • 都是正确的,这就是为什么我提出我之前对这个问题的评论。他想要一个完美的散列函数,这不太可能减小大小。
    • 嘿,我可能使用 hashCode() 结果作为键(我面前没有代码),所以修复起来很简单。尽管如此,我仍然感谢您付出额外的努力来回答这个问题并使用位移位!
    猜你喜欢
    • 2011-01-23
    • 1970-01-01
    • 2023-04-08
    • 2013-01-04
    • 1970-01-01
    • 1970-01-01
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多