简体   繁体   English

如何在Java中为其他属性列表的属性编写equals和hashCode方法

[英]How to write equals and hashCode methods in java for attribute that is a list of other attributes

I have a HashMap where the key is a class and value is an integer. 我有一个HashMap ,其中的键是一个类,值是一个整数。 I need to check if an object of the class already exists in the map. 我需要检查地图中是否已存在该类的对象。 I use containsKey() , but for some reason it does not work when I include attribute sideDish in the equals() and hashCode() . 我使用containsKey() ,但是由于某些原因,当我在equals()hashCode()包含属性sideDish时,它不起作用。 Here is my code for the classes: 这是我的课程代码:

OrderItem class: 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;
    }
}

Food class: 食品类:

@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;
    }
}

SideDish class: SideDish类:

@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;
    }
}

For some reason, if I removethe sideDish attribute from equals() and hashCode() in the OrderItem class, it works perfectly. 出于某种原因,如果我从OrderItem类的equals()hashCode()sideDish属性,则它可以正常工作。

But I also need sideDish to be checked as part of the object identity. 但是我还需要将sideDish作为对象标识的一部分进行检查。

Here is how I use it: 这是我的用法:

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());
        }
    }
}

In your OrderItem class, both your hashCode() and equals() depend on the property List<SideDish> sideDishes . OrderItem类中, hashCode()equals()取决于属性List<SideDish> sideDishes

Thus, if sideDishes changes, so does the hashCode() (and so does equality). 因此,如果sideDishes更改,则hashCode() sideDishes更改(相等性sideDishes更改)。


A HashMap uses both hashCode() and equals() to store and find the object which is the key . HashMap使用hashCode()equals()来存储和查找作为key的对象。 It uses a concept called " hash buckets ". 它使用了一个称为“ 哈希桶 ”的概念。 If you put a key into a HashMap , and then the hashCode() changes, that object will be in the wrong hash bucket, and you won't be able to find it again. 如果将密钥放入HashMap ,然后hashCode()更改,则该对象将位于错误的哈希存储桶中,并且您将无法再次找到它。

A key is something which is used for lookup purposes - that's what the word "key" means. 密钥是用于查找目的的东西,这就是“密钥”一词的含义。 An important quality of a key, whether in a database, or a hashmap, is immutability. 密钥的重要质量,无论是在数据库中还是在哈希图中,都是不可变性的。 So in Java, that means an object which changes its hashCode() makes for a bad key. 因此,在Java中,这意味着更改其hashCode()的对象将产生错误的密钥。

It's a bit like if a file system did lookups by the hash of the filename, but then you changed the filename, but it didn't update the hash. 这有点像文件系统通过文件名的哈希进行查找,但是随后您更改了文件名,但没有更新哈希。 You'd only find the file by doing a lookup with the old name. 您只能通过使用旧名称进行查找来找到文件。


This simple test program will illustrate the point. 这个简单的测试程序将说明这一点。

We store 2 objects in a HashMap , and then change the hashCode() . 我们将2个对象存储在HashMap ,然后更改hashCode() The map still contains both objects, but now one of them cannot be found or used for lookup. 映射仍然包含两个对象,但是现在找不到其中之一或将其用于查找。

The solution is use some simple immutable object as the key , such as a Long of its database ID. 解决方案是使用一些简单的不可变对象作为 ,例如其数据库ID的Long

Sample output is below the code. 示例输出在代码下方。

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));
   }
}

Sample output: 样本输出:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM