对于String,StringBuffer,StringBuilder作为HashMap的Key的探讨

原文: http://www.itkeyword.com/doc/1200285227501001789/string-vs-stringbuffer-as-hashmap-key

以下内容为对该文章的理解性翻译,部分认为无用的内容被删除

我试图理解为什么当用作HashMap密钥时,String和StringBuilder / StringBuffer被区别对待。让我通过以下内容使我的困惑更加清楚:

例1:使用String:

String s1 = new String("abc");
String s2 = new String("abc");
HashMap<String,Object> hm = new HashMap<>();
hm.put(s1, 1);
hm.put(s2, 2);
System.out.println(hm.size());
  • 上面的代码片段打印为“ 1”;

例2:使用StringBuilder(或StringBuffer):

StringBuilder sb1 = new StringBuilder("abc");
StringBuilder sb2 = new StringBuilder("abc");
HashMap<StringBuilder,Object> hm = new HashMap<>();
hm.put(sb1, 1);
hm.put(sb2, 2);
System.out.println(hm.size());
  • 上面的代码片段显示为“ 2”;

接下来是一些讨论(只翻译了其中的部分):

  • 问:造成这种不一致的确切区别是什么。两者都是内部char []。不是吗?

  • 答:

    • String是不可变的。StringBuilder是可变的,这意味着仅因为两个StringBuilder碰巧现在包含相同的文本,并不意味着它们将来会包含相同的文本

    • 理解:测试以下代码

      String s1 = new String("abc");
      String s2 = new String("abc");
      System.out.println(s1.hashCode());
      System.out.println(s2.hashCode());
      System.out.println("========================================");
      StringBuilder sb1 = new StringBuilder("abc");
      StringBuilder sb2 = new StringBuilder("abc");
      System.out.println(sb1.hashCode());
      System.out.println(sb2.hashCode()); 
      
    • StringBuilder / Buffer没有重写hashCode()和equals()函数。这意味着该对象的每个实例都应该是唯一的哈希码并且其值或状态无关紧要

    • 你应该使用String作为Key。StringBuilder / Buffer是可变的,将其用作HashMap的键通常不是一个好主意,因为在其下存储值可能会修改,导致以后无法访问原始值

    • StringBuilder使用Object默认的hashCode()函数实现,而String则通过比较字符串中的字符进行判断是否相等。Map的工作方式(特别是HashMap)是利用对象的hashcode,而不是类的内容(理解:其中保存的内容);

推荐:Java中String,StringBuffer,StringBuilder简述及区别

  • 查看源码:

    • 如果查看StringBuffer,它将发现它使用了Object.equals:
    public boolean equals(Object obj) {
        return (this == obj);
    }
    
    • 而String的实现是:
        public boolean equals(Object anObject) {
            if (this == anObject) {
                return true;
            }
            if (anObject instanceof String) {
                String anotherString = (String)anObject;
                int n = value.length;
                if (n == anotherString.value.length) {
                    char v1[] = value;
                    char v2[] = anotherString.value;
                    int i = 0;
                    while (n-- != 0) {
                        if (v1[i] != v2[i])
                            return false;
                        i++;
                    }
                    return true;
                }
            }
            return false;
        }
    
  • 接下来是一些争论:争论的中心点在于String的intern()那个技术点;

    • 你可以参考我的文章: https://blog.csdn.net/Hubz131/article/details/105158957
    • 如果我的文章不能是你满意,也可以自行搜索;
  • 现在,想象一下,如果我有两个具有相同哈希码值的对象,将会发生什么?在这种情况下,HashMap首先需要知道两个对象是否相同,因为如果它们相同,则意味着映射中只有一个条目,并且与该键关联的现有值将被新值替换。但是,仅具有相同的哈希码并不意味着两个密钥都相等。因此,是否相等是通过在关键对象上调用equals()方法来确定的。如果equals()返回true,则对象相等。在这种情况下,哈希映射需要更新现有条目的值。但是如果equals()返回false怎么办?在这种情况下,两个对象都不相同,应将其存储为单独的条目。因此,它们都将存储在HashMap中。 在检索值时,将使用输入值的哈希码到达存储它的隔离专区(:位桶数组的相应位置),然后,如果隔离专区包含多个对象(由于使用拉链法解决哈希碰撞的原因),则将检查输入键是否与隔离专区中的某个对象相等,如果匹配,则返回关联的值;

    • 仅具有相同的哈希码并不意味着两个密钥都相等:

    •   String s1 = new String("uP");
        String s2 = new String("v1");
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        HashMap<String,Object> hm = new HashMap<>();
        hm.put(s1, 1);
        hm.put(s2, 2);
        System.out.println(hm.size());
      
  • 现在,让我们将上述理论应用于您拥有的代码。如果字符串对象的内容相等,则它们相等。并且根据规则,如果两个对象相等,则它们应返回相同的哈希码。但是请记住,相反是不正确的。如果两个对象返回相同的哈希码,则内容不一定相等。具有相同内容的两个字符串相等,即使它们在物理上是不同的对象,它们也会返回相同的哈希码,因此在HashMap中用作键时,它们始终会映射到同一条目。因此,就会出现以上现象(指的是例1)。

  • String类会覆盖默认的equals()方法,该方法表示两个对象如果具有相同的引用,而这些引用依赖于内容的相等性,则它们相等。之所以可以这样做是因为String是不可变的。但是,StringBuffer不会这样做。它仍然依赖于引用相等(地址)。

总结

三者相比,String更适合作为HashMap的键

1、StringBuffer/Builder作为键的话,如果后期内容发生了更改,就无法使用历史插入的那个值进行查询;

2、StringBuffer/Builder如果出现hashCode一样的话,不会对内容进行比较,直接回替换另一个相同hashCode的值,违背了正常思维(我是这么理解的,希望指正)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值