【问题标题】:java hashmap keysjava hashmap键
【发布时间】:2011-03-14 00:34:45
【问题描述】:

你有什么理由使用 String 以外的任何东西作为 hashmap 键?似乎字符串在 99% 的情况下都足够好。另外,您不必实现 hashCode() 和 equals(Object o)。谢谢

【问题讨论】:

  • 有时您不想关闭字符串,因为您必须从简单的不是字符串中键入;在这些情况下,有一个很好的理由。
  • String 不是“99% 的时间都足够好”。 String 没有特别常见的原因。

标签: java hashmap key


【解决方案1】:

想象一下您要创建一个以用户类型和名称为键的地图的情况。在这种情况下,您最好编写一个由 2 个字段组成的类(并覆盖 hashCodeequals)。在这种情况下使用单个字符串,并尝试以某种方式将这 2 条信息组合成单个字符串会非常尴尬且容易出错。

【讨论】:

    【解决方案2】:

    就像 Mark Elliot 所说,有时会有一个自然键,它没有自然的字符串形式。为什么不使用它?

    当您有一个方便的字符串形式或一个显而易见的形式时,字符串就足够了,例如当您处理数据库实体时。

    但是,性能是另一个原因。

    当然,如果您确实实现了自定义键,则必须绝对确保它是不可变的。正如垃圾神所说,这不仅是一种偏好。此外,Comparable 也无关紧要。

    如果您在哈希映射中有一个可变键,您可以完全破坏您的哈希映射。

    【讨论】:

    • 另外,字符串的构造可能很昂贵。密钥必须是不可变的并且正确实现 hashcode/equals。如果未指定 Comparator(自然排序),则 Comparable 与 SortedMap 相关。
    【解决方案3】:

    字符串可以涵盖很多用例,但是使用更复杂的对象可以为您的代码提供更多的灵活性。看到在美国他们刚刚公布了 NCAA 大学篮球锦标赛的种子,我看看能不能想出一个以锦标赛为主题的例子。

    假设我想构建一个 Hashmap 来存储每个区域中的所有团队。比赛有四个地区:东部,西部,西南和东南部。您当然可以将地图创建为:

    HashMap<String, List<Team>> teamsInRegions = new HashMap<String, List<Team>>();
    

    但是,如果您已经有一个表示区域的对象怎么办?您可能会这样做,因为每个地区的相关信息比名称要多得多。例如,我认为 Region 类可能如下所示:

    public class Region {
        private String name;
    
        private Calendar firstRound;
        private String firstRoundLocation;
    
        private Calendar secondRound;
        private String secondRoundLocation;
    
        private Calendar thirdRound;
        private String thirdRoundLocation;
    
        ....
    }
    

    你明白了。如果 HashMap 由字符串作为键,您当然可以使用合理的 Region.toString() 方法并将其用作键,但使用更复杂的对象将允许您编写更灵活的代码。

    虽然其他人可能不同意,但我发现自己有时会以某种不可预测的方式使用 Maps,调用 HashMap.keySet() 会为我提供一组完全填充的复杂对象,其中包含大量信息。 并且拥有地图意味着我也可以使用这些对象在其他地方做出决策。

    例如,假设我想建立一个包含篮球锦标赛所有比赛地点的列表。如果我的 HashMap 包含复杂的对象,那将很简单:

    Set<Region> regions = myMap.keyset();
    Set<String> gameLocations = new HashSet();
    for (Region region : regions) {
        gameLocations.add(region.getAllLocales());
    }
    

    等等。 YMMV。

    【讨论】:

    • 还有一点需要注意:创建 equals/hashcode 不应该成为阻止您使用复杂对象的问题。大多数 IDE 都有一个简单的过程,可以根据类的成员变量为您生成这些方法。非常方便,而且您不必担心会搞砸一些简单的事情。
    【解决方案4】:

    有时你想要一个对象而不是字符串

    假设您有一张...的地图,比如一张顾客和餐厅订单的地图(字典或其他哈希表)。

    而不是拥有

    Customer c = new Customer("Bob");
    Order o = new Order("Fries");
    HashMap<string, Order> map = new HashMap<string, Order>();
    map.put(c.Name, o);
    

    您可以使用 Customer 对象访问所有内容

    Hashmap<Customer, Order> map = new HashMap<Customer, Order>();
    map.put(c, o);
    

    现在你为什么更喜欢这个?

    也许在您的餐厅中,在那个特定的晚上您有多个名为“Bob”的客户,因此仅拥有名称的 HashMap 已经不够了。您的 Customer 类将反映“Bob”之间的差异,但仅代表它们中的每一个的字符串是行不通的。

    通过拥有客户的 HashMap,您可以简单地更改客户类的哈希算法,一切都会完美运行(希望如此)。如果你一直使用字符串,那么修改和唯一标识每个对象实例就不会那么容易了

    【讨论】:

      【解决方案5】:

      字符串可能是最常见的用例,但它实际上取决于您要存储的内容。您不希望将 Long 转换为 String 只是为了将您的密钥保留为 String。只需使用数据结构建议的类型即可。对于类型,例如长你也不必实现 hashCode/equals 。如果您正在考虑实现 equals 和 hashCode 的自定义类,无论如何可能是个好主意。用例如实现它Apache Commons' EqualBuilder 也很简单。

      【讨论】:

        【解决方案6】:

        我想您可以说 String 已经“足够好”,因为您可以将典型的键类型转换为字符串。但是,相对于使用真正的键类型作为哈希表键的成本,这样做可能会很昂贵。例如,每次您想将Integer(或int)用作哈希表键时,将其转换为字符串...

        另一点是在键类型上实现hashCodeequals 非常简单。事实上如此简单,以至于像 Eclipse 这样的 IDE 能够为您生成这两种方法。

        【讨论】:

          【解决方案7】:

          优点:
          1) 字符串的不可变 => 线程安全,没有并发问题
          2) String 的 hashcode 被缓存(在 JDK 中寻找 String 类的 private int hash)。节省大量处理时间。
          3) 字符串池 用于文字(字符串对象的重用)或使用 new 运算符创建的实习生字符串以将它们添加到池中
          4) 安全性,没有人可以编辑密钥
          5)Equals和hashcode实现得很好

          缺点:
          1) 字符串池会带来安全风险,即会将明文密码暴露给任何有权访问 java 应用程序内存的人。 java 应用程序的核心转储,在 /tmp 中生成内存转储可以使密码成为真正的威胁。
          解决方案:对于密码使用 char[],您可以通过设置清除修道院它为空白或任何其他可降低暴露密码安全风险的字符。

          【讨论】:

            猜你喜欢
            • 2011-10-09
            • 2011-03-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-15
            • 1970-01-01
            相关资源
            最近更新 更多