Comment mettre en œuvre correctement equals (), hashCode () Tree en Java?

FreeOnGoo:

J'ai une structure arborescente et je dois remplacer les méthodes equals / hashCode parce que j'utilise la vérification du résultat attendu dans les tests unitaires.

Le problème avec des structures de type arbre est qu'ils se réfèrent les uns aux autres récursive. En particulier, les parents pour les enfants et vice-versa.

et si tous les champs sont utilisés dans les méthodes equals / hashCode, alors il y aura une mise en boucle. La question est de savoir comment remplacer correctement, alors pour ne pas violer le contrat.

Je vais donner un exemple de la façon dont j'implémenté.

public class App {
    public static void main(String[] args) {
        Book book1 = new Book(1L, "The catcher in the rye");
        Book book2 = new Book(2L, "Rich Dad Poor Dad");

        BookTree bookTree1 = new BookTree(book1);
        BookTree bookTreeChild1 = new BookTree(book2);
        bookTree1.addChild(bookTreeChild1);

        BookTree bookTree2 = new BookTree(book1);
        BookTree bookTreeChild2 = new BookTree(book2);
        bookTree2.addChild(bookTreeChild2);

        if (!bookTree1.equals(bookTree2)) {
            throw new RuntimeException("Invalid override equals");
        }
    }
}

class Book {
    private Long id;
    private String name;

    public Book(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) return true;
        if (object == null || getClass() != object.getClass()) return false;
        Book book = (Book) object;
        return Objects.equals(id, book.id) &&
                Objects.equals(name, book.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

class Tree<T> {
    private List<Tree<T>> children = new ArrayList<>();
    private Tree<T> parent = null;
    private T data;

    public Tree(T data) {
        this.data = data;
    }

    public Tree(T data, Tree<T> parent) {
        this.data = data;
        parent.addChild(this);
    }

    public List<Tree<T>> getChildren() {
        return children;
    }

    public void addChild(Tree<T> child) {
        child.setParent(this);
        this.children.add(child);
    }

    public void addChild(T data) {
        Tree<T> newChild = new Tree<>(data);
        this.addChild(newChild);
    }

    public void removeChildren() {
        this.children = new ArrayList<>();
    }

    public void addChildren(List<Tree<T>> children) {
        for(Tree<T> t : children) {
            t.setParent(this);
        }
        this.children.addAll(children);
    }

    private void setParent(Tree<T> parent) {
        this.parent = parent;
    }

    public Tree<T> getParent() {
        return parent;
    }

    public T getData() {
        return this.data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public boolean isRoot() {
        return (this.parent == null);
    }

    public boolean isLeaf() {
        return this.children.size() == 0;
    }

    public void removeParent() {
        this.parent = null;
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) return true;
        if (object == null || getClass() != object.getClass()) return false;
        Tree<?> tree = (Tree<?>) object;
        return Objects.equals(children, tree.children) &&
                Objects.equals(data, tree.data);
    }

    @Override
    public int hashCode() {
        return Objects.hash(children, data);
    }
}

class BookTree extends Tree<Book> {

    public BookTree(Book data) {
        super(data);
    }

    public BookTree(Book data, Tree<Book> parent) {
        super(data, parent);
    }
}

Comme vous pouvez le voir dans ma mise en œuvre, j'utilise deux champs: « données » et « enfants ». Par conséquent, ma question est de savoir si je mis à exécution les méthodes equals / hashCode correctement? Si elle est mauvaise, alors s'il vous plaît montrer comment.

GhostCat de Monica C.:

Par conséquent, ma question est de savoir si je mis à exécution les méthodes equals / hashCode correctement?

Tout d' abord: « ce qui est correct? » ... on pourrait se demander pourquoi un arbre devrait mettre en œuvre equals()et hashCode()en premier lieu. En particulier hashCode()est délicate: le point de cette méthode est (principalement) de sorte que vous pouvez stocker l'objet correspondant dans un HashMap / HashSet. Mais cela soulève un grand drapeau rouge: ces deux classes ne pas comme ça, quand les hashCode()rendements des valeurs différentes au fil du temps. Et c'est exactement ce que votre code sera fait: à chaque fois que vous changez votre arbre (ajout / suppression d' un nœud), hashCode()donnera un résultat différent.

On pourrait donc jeter un oeil à ce que les libs standards font: et là nous trouvons DTree ... qui ne met pas en œuvre les deux méthodes! D'autre part, quand on regarde vers AbstractSet (qui est la classe de base pour TreeSet), il nous constatons que les deux méthodes sont mises en œuvre et comprend les membres. Ainsi , les deux voies semblent valides.

Pour en revenir à la question: cela dépend vraiment comment vous voulez ces deux méthodes pour. Deux arbres égaux quand ils ont exactement le même contenu ( ce qui signifie: ne l'ordre des enfants d' importance)?

Longue histoire courte: en supposant que vous voulez vous assurer que toutes les données est égal, et que tous les enfants sont égaux, et dans le même ordre, votre mise en œuvre semble correcte.

Et oui, cette restriction pour vérifier que ces deux attributs fait beaucoup de sens: lorsque vous incluez le lien parent, vous obtenez immédiatement dans une récursion qui ne peut être rompu.

Enfin: vous avez marqué cette question avec JUnit. Cela implique que vous considérez comme des tests d' écriture pour votre code de production. Ensuite , ces tests doivent répondre à votre question. Signification: Une approche serait que vous vous asseyez et définir le contrat pour ces deux méthodes. Et puis vous créez un certain nombre de cas de test qui vérifient tous les aspects de ces contrats. Et puis vos cas de test vous dire si votre code de production répond à votre contrat.

Je pense que c'est le point crucial ici: il n'y a pas de règle universelle qui nous indique si / comment mettre en œuvre equals()et hashCode()pour une classe d' arbres. Vous devez examiner vos besoins si / comment faire cela. Ensuite , vous dérivez des tests de cette connaissance, que vous vous appliquez afin de vérifier si une mise en œuvre donnée répond aux exigences / contrat.

Je suppose que tu aimes

Origine http://43.154.161.224:23101/article/api/json?id=236319&siteId=1
conseillé
Classement