hashmap的key规范

hashmap的key规范

参考文章:

https://mp.weixin.qq.com/s/FYBCLl9aaV0VE4JpMMWmsg

说明:

这篇文章写的非常好,介绍了hashcode和equals方法,面试中按照这个答即可。

前置知识

我们知道java所有类终极父类都是Object,如果定义一个类,无论它继承那个类,最好的父类都是继承Object。如果单独建一个类,它不显示的继承某个类,那么它是继承Object类的,一般我们不会显示的写继承Object,这是一个墨守成规的规则

image-20210909233921261

image-20210909234050530

Object的hashcode和equals方法

hashcode方法是调用底层c或c++方法的,因为方法使用native修饰的

public native int hashCode();

equals是比较两个地址的值

  public boolean equals(Object obj) {
        return (this == obj);
    }

hashcode()和equals方法*

hashcode()

  1. hashCode 的存在主要用于查找的快捷性,如 Hashtable, HashMap 等,hashCode 是用来在三列存储结构中确定对象的存储地址的。
  2. 如果两个对象相同,就是适用于 euqals(java.lang.Object) 方法,那么这两个对象的 hashCode一定相同。
  3. 如果对象的euqals 方法被重写,那么对象的 hashCode 也尽量重写,并且产生 hashCode 使用的对象,一定要和 equals 方法中使用的一致,否则就会违反上面提到的第二点。
  4. 两个对象的 hashCode 相同,并不一定表示这两个对象就相同,也就是不一定适用于equals() 方法,只能够说明这两个对象在三列存储结构中,如 Hashtable.,他们存在同一个篮子里。以上话以前摘录自一篇博客,讲的非常好。

equals(Object obj)

  1. 如果一个类没有重写 equals(Object obj)方法,则等价于通过 == 比较两个对象,即比较的是对象在内存中的空间地址是否相等。
  2. 如果重写了equals(Object ibj)方法,则根据重写的方法内容去比较相等,返回 true 则相等,false 则不相等。

public class MyClass {
 public static void main(String[] args) {
     HashSet books=new HashSet();
     books.add(new A());
     books.add(new A());
     books.add(new B());
     books.add(new B());
     books.add(new C());
     books.add(new C());
     System.out.println(books);
 }
}
class A{
 //类A的 equals 方法总是返回true,但没有重写其hashCode() 方法
 @Override
 public boolean equals(Object o) {
     return true;
 }
}
class B{
 //类B 的hashCode() 方法总是返回1,但没有重写其equals()方法
 @Override
 public int hashCode() {
     return 1;
 }
}
class C{
 public int hashCode(){
     return 2;
 }

 @Override
 public boolean equals(Object o) {
     return true;
 }
}

打印结果

[com.sh.demo.B@1, com.sh.demo.B@1, com.sh.demo.C@2, com.sh.demo.A@5b37e0d2, com.sh.demo.A@69663380]

结果

  1. 即使两个A 对象通过 equals() 比较返回true,但HashSet 依然把他们当成 两个对象,即使两个 B 对象 的hashCode() 返回值相同,但HashSet 依然把他们当成两个对象。
  2. 即也就是,当把一个对象放入HashSet 中时,如果需要重写该对象对应类的 equals() 方法,则也应该重写其 hashCode() 方法。规则是:如果两个对象通过 equals() 方法比较返回true,这两个对象的 hashCode 值也应该相同。
  3. 如果两个对象通过euqals() 方法比较返回true,但这两个对象的 hashCode() 方法返回不同的hashCode 值时,这将导致HashSet 会把这两个对象保存在 Hash 表的不同位置,从而使两个对象都可以添加成功,这就与 Set 集合的规则冲突了。
  4. 如果两个对象的 hashCode() 方法返回的 hasCode 值相同,但他们通过 equals() 方法比较返回false 时将更麻烦:因为两个对象的hashCode 值相同,HashSet 将试图 把它们保存在同一个位置,但又不行(否则将只剩下一个对象),所以实际上会在这个位置用链式结构来保存多个对象;而HashSet 访问集合元素时也是根据元素的 hashCode 值来快速定位的,如果 hashSet 中两个以上的元素具有相同的 HashCode 值时,将会导致性能下降。

用Object做hashMap的Key时需要做什么?

前置说明:

hashset是基于hashmap的,我们查看源码得到

private static final Object PRESENT = new Object();
....
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

用自定义类作为key,必须重写equals()和hashCode()方法。

自定义类中的equals() 和 hashCode()都继承自Object类。

t(e, PRESENT)==null;
}


用自定义类作为key,必须重写equals()和hashCode()方法。

自定义类中的equals() 和 hashCode()都继承自Object类。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这次的文章标题原本想叫《记一次因错用HashMap造成的生产BUG》,但作者觉得这个标题可能会让人联想到某些Java公众号写的标题党文章,所以改成了现在这个标题。在文章中,作者承认在A接口中将所有产品都放到HashMap中并不太规范,但在实际实现中是可行的,所以并不算是“错用”。然而,作者在写代码时疏忽了缓存的问题,忽视了HashMap在多线程环境下不能使用的要求,结果写出了有BUG的代码。 作者从这次线上BUG中学到了几个教训:首先,在使用直接存放对象引用的内存缓存时,要注意不要修改从缓存中获取的内容;其次,在修改代码时,要全面考虑前后逻辑,思考修改的后果;再次,不要困于思维陷阱,在寻找问题时不一定问题出在前面的代码;最后,遇到解决不了的问题时,寻求帮助是很重要的,领导在几分钟内就发现了作者几个小时都没找到的BUG。 问题出现在addDisplayStyle()方法中。当有多个APP请求A接口时,业务线程执行到product.put("displayStyle", displayStyle)这行代码时,实际上是在往同一个Map中放入元素。由于key相同,就会发生哈希冲突。如果此时某个Map的元素个数达到了扩容条件,就会触发扩容,并有可能出现链表成环的情况。当有新的请求到来时,工作线程执行到recommends = deduplicateAndGetFirstN(recommends, 3)这行代码。 综上所述,这次的文章是关于作者经历的一次因错用HashMap而导致生产BUG的经历。作者从中学到了关于使用缓存、修改代码、寻找问题和寻求帮助等方面的教训。问题出现在addDisplayStyle()方法中,当多个APP请求A接口时,可能会触发HashMap的扩容并导致链表成环的情况。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值