java重写equals方法为什么要重写hashcode,什么情况下要重写equals

  java重写equals方法为什么要重写hashcode,什么情况下要重写equals

  如何解决写爬虫IP受阻的问题?立即使用。

  首先告诉大家结论:

  首先要明确一点,重写equals不一定是hashcode,要看实际情况。比如不使用容器的时候不需要,但是如果使用HashMap之类的容器,使用自定义对象作为键,就必须重写。

  (学习视频分享:java视频教程)

  重写equals以确定实例在业务逻辑中是否相等。has代码被重写以使集合快速称重。

  hashCode()和equals()的规定:

  1.如果两个对象相等,那么hashcode必须相同。

  2.两个对象相等,对两个equals()方法返回true。

  3.两个对象具有相同的hashcode值,它们不一定相等

  4.综上所述,如果equals()方法已经被覆盖,那么hashCode()方法也必须被覆盖。

  5.5.hashCode()的默认行为是为堆上的对象生成唯一的值。如果hashCode()没有被覆盖,这个类的两个对象在任何情况下都不会相等(即使它们指向相同的数据)。

  下面的例子说明必须重写它。

  当一个自定义类被用作HashMap的键时,将

  如果只重写equals而不重写hashCode,就会出现逻辑错误。

  先看下面的代码。

  公共类测试{

  静态类别顺序{

  私有长orderId

  公共订单(长订单Id) {

  this.orderId=orderId

  }

  public Long getOrderId() {

  返回订单id;

  }

  public void setOrderId(Long orderId){

  this.orderId=orderId

  }

  @覆盖

  公共布尔等于(对象对象){

  if (obj!=null!(订单的对象实例)){

  返回false

  }

  返回Objects.equals(this.orderId,((Order) obj)。orderId);

  }

  @覆盖

  公共字符串toString() {

  返回“订单”

  订单Id=订单Id

  };

  }

  }

  公共静态void main(String[] args) {

  MapOrder,String map=new HashMap();

  订单order1=新订单(1000000001 l);

  订单order2=新订单(1000000001 l);

  map.put(order1, );

  map.put(order2, );

  system . out . println(map);

  }

  }运行输出:

  {order {orderid=100000001}=,order {orderid=100000001}=}重写了代码中的equals方法,但没有重写hashCode方法。

  equals重写的逻辑是,只要orderId相等,这两个对象就相等。

  从运行结果来看,具有相同orderId的两个对象被成功地放入map。这是一个逻辑错误,因为从逻辑上讲,预期结果在映射中应该只有一个顺序。

  让我们来看看HashMap的源代码。

  你只需要看有写评论的判决书就可以了。

  公共V put(K键,V值){

  返回putVal(hash(key),key,value,false,true);

  }

  静态最终int哈希(对象键){

  int h;

  return (key==null)?0:(h=key . hashcode())^(h 16);

  }

  final V putVal(int hash,K key,V value,boolean onlyIfAbsent,

  布尔逐出){

  NodeK,V[]tab;NodeK,V p;int n,I;

  if((tab=table)==null (n=tab . length)==0)

  n=(tab=resize())。长度;

  //通过哈希计算索引。如果索引的值为==null,则将它直接插入到索引位置。

  if ((p=tab[i=(n - 1) hash])==null)

  tab[i]=newNode(hash,key,value,null);

  否则{

  NodeK,V e;K k

  if (p.hash==hash

  ((k=p.key)==key (key!=null key.equals(k))))

  e=p;

  else if(p TreeNode的实例)

  e=((TreeNodeK,V)p)。putTreeVal(this,tab,hash,key,value);

  否则{

  for(int bin count=0;binCount) {

  if ((e=p.next)==null) {

  p.next=newNode(hash,key,value,null);

  第一个if(bin count=tree ify _ THRESHOLD-1)//-1

  treeifyBin(tab,hash);

  打破;

  }

  如果(例如,哈希==哈希

  ((k=e.key)==key (key!=null key.equals(k))))

  打破;

  p=e;

  }

  }

  如果(e!=null) { //键的现有映射

  旧值=电子值

  如果(!onlyIfAbsent oldValue==null)

  e .值=值;

  afterNodeAccess(e);

  返回旧值;

  }

  }

  modCount

  如果(大小阈值)

  resize();

  afterNodeInsertion(驱逐);

  返回空

  }通过源码我们知道,只要混杂码不一样的话就可以直接插入到数组中。然而正因为我们没重写散列码方法,所以调用的是目标的散列码方法。而目标的散列码是使用对象在堆中的地址通过算法得出一个(同国际组织)国际组织类型的值,既然如此,那刚刚创建的两个对象的(同国际组织)国际组织类型的值肯定是不同的,所以两个命令都可以正常插入到数组中,从而出现了逻辑错误。

  重写散列码方法:

  公共类TestHash {

  静态类别顺序{

  私有长订单编号

  公共订单(长订单Id) {

  this.orderId=orderId

  }

  public Long getOrderId() {

  返回订单id;

  }

  public void setOrderId(Long orderId){

  this.orderId=orderId

  }

  @覆盖

  公共布尔等于(对象对象){

  if (obj!=null!(订单的对象实例)){

  返回错误的

  }

  返回Objects.equals(this.orderId,((Order) obj).orderId);

  }

  @覆盖

  public int hashCode() {

  //这里简单重写下实际开发根据自己需求重写即可。

  返回这个。订单编号。int value()2;

  }

  @覆盖

  公共字符串toString() {

  返回"订单"

  订单Id=订单身份

  };

  }

  }

  公共静态void main(String[] args) {

  MapOrder,String map=new HashMap();

  订单订单1=新订单(1000000001 l);

  订单订单2=新订单(1000000001 l);

  map.put(order1, );

  map.put(order2, );

  系统。出去。println(地图);

  }

  }再次运行输出:

  {Order{orderId=1000000001}=}我们简单看下源码(为了好理解,我只截取了重点代码):以下单2作为注释讲解。

  final V putVal(int hash,K key,V value,boolean onlyIfAbsent,

  布尔逐出){

  NodeK,V[]tab;NodeK,V p;int n,I;

  if((tab=table)==null (n=tab。长度)==0)

  n=(tab=resize()).长度;

  //重写散列码之后两个对象的订单编号相同,hashCode也肯定相同。

  //通过混杂算出索引通过索引取值有值不进入如果。

  if ((p=tab[i=(n - 1) hash])==null)

  tab[i]=newNode(hash,key,value,null);

  否则{

  NodeK,V e;负担

  //由于重写了散列码旧对象的散列码和新的肯定相等

  if (p.hash==hash

  //(k=p.key)==key==false因为比较的是对象地址

  //(关键!=null key.equals(k))==true因为重写了等于订单编号相等则相等

  ((k=p.key)==key (key!=null key.equals(k))))

  //保存旧结节

  e=p;

  .

  如果(e!=null) { //键的现有映射

  旧值=电子值

  如果(!onlyIfAbsent oldValue==null)

  //值覆盖旧结节的值

  e .值=值;

  afterNodeAccess(e);

  返回旧值;

  }

  }

  .

  }所以订单2覆盖了订单1 .这就是为什么当使用自定义对象作为模拟的钥匙时如果重写了等于要同时哈希码。

  反过来说:重写了哈希码,等于需要重写吗?

  答案是要的,都要重写!

  以上面的代码重写逻辑为例。如果两个具有相同hashCode的对象在放置order1时具有相同的hash和相同的索引,则可以得到order 1。得到之后,你会继续使用equals比较。如果没有重写,就是对象地址比较,结果肯定是假的。然后,此时发生哈希碰撞,形成链表。

  使用map.get(key)也是一样的。会根据hashCode找到,然后判断equals。

  为什么法官平等?因为是根据hashCode找到的链表,所以需要根据equals在链表中找到带有equal Key的值。

  什么情况下将使用自定义类作为键?

  最常见的关键字是坐标,例如将对象放置在地图上的坐标处。

  公共类测试{

  静态类别坐标{

  公共坐标(int x,int y) {

  this.x=x

  this.y=y

  }

  私有int x;

  私有int y;

  public int getX() {

  返回x;

  }

  public void setX(int x) {

  this.x=x

  }

  public int getY() {

  返回y;

  }

  公共空集合(int y) {

  this.y=y

  }

  }

  公共静态void main(String[] args) {

  MapCoordinate,String map=new HashMap();

  Map.put(新坐标(22,99),‘手机’);

  Map.put(新坐标(44,48),计算机);

  }

  }相关推荐:java入门教程以上就是为什么java在重写equals时会重写hashcode?更多详情请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: