Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialization of JavaScript array to Java LinkedHashSet using Jackson and Spring doesn't remove duplicates

Let's say I have this client-side JSON input:

{
   id: "5",
   types: [
      {id: "1", types:[]},
      {id: "2", types:[]},
      {id: "1", types[]}
   ]
}

I have this class:

class Entity {
    private String id;
    private Set<Entity> types = new LinkedHashSet<>();

    public String getId() {
        return this.id;
    }

    public String setId(String id) {
        this.id = id;
    }

    public Set<Entity> getTypes() {
        return types;
    }

    @JsonDeserialize(as=LinkedHashSet.class)
    public void setTypes(Set<Entity> types) {
        this.types = types;
    }

    @Override
    public boolean equals(Object o){
        if (o == null || !(o instanceof Entity)){
            return false;
        }
        return this.getId().equals(((Entity)o).getId());
    }
}

I have this Java Spring endpoint where I pass the input in the body of a POST request:

@RequestMapping(value = "api/entity", method = RequestMethod.POST)
public Entity createEntity(@RequestBody final Entity in) {
    Set<Entity> types = in.getTypes();
    [...]
}

I would like in:

Set<Entity> types = in.getTypes();

to have only two entries in the correct order... since one of them is a duplicate based on the id... Instead I get duplicates in the LinkedHashSet (!)

I thought from the code I have that removing duplicates would work automatically, but apparently it is not.

This question has a broader context than Why do I need to override the equals and hashCode methods in Java? since it is using implicit Jackson serialization through Java Spring.

like image 924
Michail Michailidis Avatar asked Dec 31 '16 08:12

Michail Michailidis


2 Answers

Only overriding equals method will not work because hash-based collections use both the equals and hashCode method to see if two objects are the same. You'll need to override hashCode() method in Entity class as both hashCode() and equals() method are required to be properly implemented to work with Hash based collections.

If your requirement is that if the some or all of the fields of two objects of the Entity class are same, then the two objects are to be considered equivalent, in that case, you'll have to override both equals() and hashCode() method.

For e.g. - if only id field in the Entity class is required to determine if the two objects are equal, then you'll override equals(), something like this:

@Override
public boolean equals(Object o) {
    if (this == o)
        return true;
    if (o instanceof Entity){
        Entity that = (Entity) o;
        return this.id == null ? that.id == null : this.id.equals(that.id);
    }
    return false;

}

but along with it, the hashCode() method need to be overridden in a way to produce same hashcode if id has the same value, maybe something like this:

@Override
public int hashCode() {
    int h = 17;
    h = h * 31 + id == null ? 0 : id.hashCode();
    return h;
}

Only now it'll work properly with Hash based collections, as both these methods are used to identify an object uniquely.


More on this:

  • Relationship between hashCode and equals method in Java
  • Why do I need to override the equals and hashCode methods in Java?
like image 109
Gurwinder Singh Avatar answered Oct 25 '22 01:10

Gurwinder Singh


Assuming that if the members of Entity class i.e. the id and type are same then the object of class Entity is same is completely wrong unless and until you override the hashcode() and equals() function explicitly.

If you do not override the hashCode() and equals() function in your Entity class then the two objects will be different even though they have same data within their members.

like image 22
user2004685 Avatar answered Oct 25 '22 01:10

user2004685