【问题标题】:How to write equals and hashCode methods in java for attribute that is a list of other attributes如何在java中为作为其他属性列表的属性编写equals和hashCode方法
【发布时间】:2018-06-07 07:11:48
【问题描述】:

我有一个HashMap,其中键是类,值是整数。我需要检查该类的对象是否已存在于地图中。我使用containsKey(),但由于某种原因,当我在equals()hashCode() 中包含属性sideDish 时它不起作用。这是我的课程代码:

OrderItem 类:

@ToString
@Entity
@Table(name="OrderItem")
public class OrderItem implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Getter @Setter 
    private Long id;

    @ManyToOne
    @Getter @Setter 
    private Food food;

    @ManyToMany
    @Getter @Setter 
    private List<SideDish> sideDishes;

    public OrderItem() {}

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((food == null) ? 0 : food.hashCode());
        result = prime * result + ((sideDishes == null) ? 0 : sideDishes.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        OrderItem other = (OrderItem) obj;
        if (food == null) {
            if (other.food != null)
                return false;
        } else if (!food.equals(other.food))
            return false;
        if (sideDishes == null) {
            if (other.sideDishes != null)
                return false;
        } else if (!sideDishes.equals(other.sideDishes))
            return false;
        return true;
    }
}

食品类:

@ToString
@Entity
@Table(name="Food")
public class Food implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Getter @Setter 
    private Long id;

    @Column(nullable = false, unique = true)
    @NotNull(message = "Name cannot be null.")
    @Getter @Setter 
    private String name;

    @ManyToMany
    @Getter @Setter
    private List<SideDish> sidedishes;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((foodtype == null) ? 0 : foodtype.hashCode());
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Food other = (Food) obj;
        if (foodtype == null) {
            if (other.foodtype != null)
                return false;
        } else if (!foodtype.equals(other.foodtype))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

配菜类:

@Entity
@ToString(exclude= {"id","dishtype"})
@Table(name="SideDish")
public class SideDish implements Serializable, Comparable<SideDish>{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Getter @Setter
    private Long id;

    @Getter @Setter
    @Column(nullable = false, unique = true)
    private String name;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        SideDish other = (SideDish) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

出于某种原因,如果我从 OrderItem 类中的 equals()hashCode() 中删除 sideDish 属性,它会完美运行。

但我还需要将 sideDish 作为对象身份的一部分进行检查。

这是我的使用方法:

HashMap<OrderItem, Integer> orderItemsToSend = new HashMap<OrderItem, Integer>();

for (Order order : orders) {
    for (OrderItem orderItem : order.getOrderItems()) {
        int numSimilarOrders = getNumOfSimilarOrders(orderItem, orders);
        if(!orderItemsToSend.containsKey(orderItem)) {
            orderItemsToSend.put(orderItem, numSimilarOrders);
        }else {
            System.out.println("Vec je dodat item koji isti kao: " + orderItem.getFood().getName());
        }
    }
}

【问题讨论】:

  • 定义“它不起作用”——equals 和 hashCode 有什么问题?
  • 它只是没有正确比较对象。它让我可以在我的地图中放置 2 个相同的对象,当然他认为它们不相等,但它们的字段中具有相同的值,至少我在 equals 和 hashCode 中使用的字段
  • 举一个例子来说明这个问题。请阅读有关如何创建minimal reproducible example 的部分。
  • @P3P5 您对sideDishes 字段使用了哪些List 实现?您正在使用的具体(完全合格)类是什么?将密钥添加到 Map 后是否更改对象值?

标签: java hashmap hashcode


【解决方案1】:

在您的OrderItem 类中,您的hashCode()equals() 都依赖于属性List&lt;SideDish&gt; sideDishes

因此,如果sideDishes 发生变化,hashCode() 也会发生变化(相等性也会发生变化)。


HashMap 使用 hashCode()equals() 来存储和查找作为 key 的对象。它使用了一个名为“hash buckets”的概念。如果您将 key 放入 HashMap,然后 hashCode() 更改,则该对象将位于错误的哈希桶中,您将无法再次找到它。

key 是用于查找目的的东西——这就是“key”这个词的意思。无论是在数据库中还是在 hashmap 中,key 的一个重要特性是不可变性。因此,在 Java 中,这意味着更改其hashCode() 的对象会产生错误的键。

这有点像文件系统通过文件名的哈希值进行查找,但是您更改了文件名,但它没有更新哈希值。您只能通过使用旧名称进行查找来找到该文件。


这个简单的测试程序将说明这一点。

我们将 2 个对象存储在 HashMap 中,然后更改 hashCode()。地图仍然包含这两个对象,但现在无法找到其中一个或用于查找。

解决方案是使用一些简单的不可变对象作为key,例如其数据库ID 的Long

示例输出在代码下方。

public class HashTest {

   static class Hashable {
     String name;

    @Override
    public int hashCode() {
      return ((name == null) ? 0 : name.hashCode());
    }

    @Override
    public boolean equals(Object object) {
      return (object instanceof Hashable) && equals((Hashable) object); 
    }

    private boolean equals(Hashable that) {
      return Objects.equals(this.name, that.name);
    }

    @Override
    public String toString() {
      // Use identityHashCode() so we can really see which object is which
      return "[" + name + ":" + System.identityHashCode(this) + "]";
    }
   }

   public static void main(String[] args) {
     Hashable one = new Hashable();
     one.name = "one";
     Hashable two = new Hashable();
     two.name = "one";

     print(one, two);
     two.name = "two";
     print(one, two);

     HashMap<Hashable, Integer> map = new HashMap<>();

     map.put(one, 1);
     map.put(two, 2);
     find(map, one, two);

     one.name = "two"; // Let's confuse things
     print(one, two);
     find(map, one, two);
   }

   private static void print(Hashable one, Hashable two) {
     System.out.print("Names:" + one.name + ":" + two.name);
     System.out.print("\tHashcodes:" + one.hashCode() + ":" + two.hashCode());
     System.out.println("\tEquals:" + one.equals(two));
   }

   private static void find(HashMap<Hashable, Integer> map, Hashable one, Hashable two) {
     System.out.print(map);
     System.out.print("\tFound: " + map.get(one));
     System.out.println("\tFound: " + map.get(two));
   }
}

示例输出:

Names:one:one   Hashcodes:110182:110182 Equals:true
Names:one:two   Hashcodes:110182:115276 Equals:false
{[one:366712642]=1, [two:1829164700]=2} Found: 1    Found: 2
Names:two:two   Hashcodes:115276:115276 Equals:true
{[two:366712642]=1, [two:1829164700]=2} Found: 2    Found: 2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-08
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多