Дессериализация массива JavaScript в Java LinkedHashSet с использованием Jackson и Spring не удаляет дубликаты

Скажем, у меня есть этот клиентский вход JSON:

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

У меня есть этот класс:

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

У меня есть эта конечная точка Java Spring, где я передаю вход в тело POST запрос:

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

Я хотел бы:

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

иметь только две записи в правильном порядке... так как один из них является дубликатом на основе идентификатора... Вместо этого я получаю дубликаты в LinkedHashSet (!)

Я думал, что из кода, который у меня есть, удаление дубликатов будет работать автоматически, но, видимо, это не так.

Этот вопрос имеет более широкий контекст, чем Почему мне нужно переопределить методы equals и hashCode в Java? поскольку он использует неявную сериализацию Джексона через Java Spring.

Ответ 1

Только переопределение метода equals не будет работать, потому что коллекции на основе хэш-функции используют метод equals и hashCode, чтобы увидеть, совпадают ли два объекта. Вам нужно переопределить метод hashCode() в классе Entity, поскольку для работы с коллекциями на основе Hash требуется как метод hashCode(), так и equals().

Если ваше требование состоит в том, что если некоторые или все поля из двух объектов класса Entity одинаковы, то два объекта должны считаться эквивалентными, в этом случае вам придется переопределить оба equals() и hashCode().

Например, - если в классе Entity требуется только поле id, чтобы определить, равны ли эти два объекта, вы переопределите equals(), что-то вроде этого:

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

}

но вместе с ним метод hashCode() должен быть переопределен таким образом, чтобы создать тот же хэш-код, если id имеет одинаковое значение, может быть, что-то вроде этого:

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

Только теперь он будет корректно работать с коллекциями на основе Hash, поскольку оба эти метода используются для однозначного определения объекта.


Подробнее об этом:

Ответ 2

Предполагая, что если члены класса Entity, то есть id и type, являются одинаковыми, то объект класса Entity является таким же, что и полностью, если только вы не переопределите hashcode() и equals() функция явно.

Если вы не переопределите функцию hashcode() и equals() в классе Entity, тогда два объекта будут отличаться, хотя они имеют одинаковые данные в своих членах.

Ответ 3

В Java, равенство объектов определяется переопределением контракта equals() и hashcode() .

В Object имеются реализации по умолчанию equals() и hashcode() в Object. Если вы не предоставите свою собственную реализацию, они будут использованы. Для equals() это означает сравнение ==: объекты будут равны, если они являются точно одним и тем же объектом.

Отвечайте на свой вопрос. Объекты в LinkedHashSet наследуют поведение по умолчанию методов eqauls() и hashcode() из класса Object. Переопределить eqauls() и hashcode() класса Entity LinkedHashSet

См. ниже this для поведения по умолчанию hashcode() и equals().