Apache Commons égale / hashCode builder [fermé]

je suis curieux de savoir, ce que les gens ici pensent de l'utilisation org.apache.commons.lang.builder EqualsBuilder / HashCodeBuilder pour la mise en œuvre du equals / hashCode ? Serait-il préférable d'écrire vos propres? Ça marche bien avec hibernation? Quel est votre avis?

150
demandé sur palacsint 2011-02-18 09:10:15

8 réponses

Les communes/lang constructeurs sont grands et j'ai été de les utiliser pendant des années sans notable des performances (avec et sans la mise en veille). Mais comme L'écrit Alain, La Voie de la goyave est encore plus belle:

voici un échantillon de haricot:

public class Bean{

    private String name;
    private int length;
    private List<Bean> children;

}

Voici equals() et hashCode (), mis en œuvre avec les Communes/Lang:

@Override
public int hashCode(){
    return new HashCodeBuilder()
        .append(name)
        .append(length)
        .append(children)
        .toHashCode();
}

@Override
public boolean equals(final Object obj){
    if(obj instanceof Bean){
        final Bean other = (Bean) obj;
        return new EqualsBuilder()
            .append(name, other.name)
            .append(length, other.length)
            .append(children, other.children)
            .isEquals();
    } else{
        return false;
    }
}

et ici avec Java 7 ou plus (inspiré par la goyave):

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

@Override
public boolean equals(final Object obj){
    if(obj instanceof Bean){
        final Bean other = (Bean) obj;
        return Objects.equals(name, other.name)
            && length == other.length // special handling for primitives
            && Objects.equals(children, other.children);
    } else{
        return false;
    }
}

Note: ce code référait initialement à la goyave, mais comme les commentaires l'ont souligné, cette fonctionnalité a depuis été introduite dans le JDK, donc la goyave n'est plus nécessaire.

comme vous pouvez le voir la version Guava / JDK est plus courte et évite les objets d'aide superflus. En cas d'égalité, il permet même de court-circuiter l'évaluation si un appel antérieur Object.equals() retourne false( pour être honnête: commons / lang a une méthode ObjectUtils.equals(obj1, obj2) avec une sémantique identique qui pourrait être utilisé au lieu de EqualsBuilder pour permettre le court-circuitage comme ci-dessus).

donc: oui, les constructeurs de Lang commons sont très préférables aux méthodes equals() et hashCode() construites manuellement (ou ces horribles monstres Eclipse vont générer pour vous), mais les versions Java 7+ / Guava sont encore meilleures.

et une note sur L'hibernation:

faites attention à l'utilisation de collections paresseuses dans vos égaux (), hashCode () et toString() des implémentations. Cela échouera lamentablement si vous n'avez pas une séance ouverte.


Note (sur equals()):

a) dans les deux versions de equals() ci-dessus, vous pouvez utiliser l'une ou l'autre de ces raccourcis aussi:

@Override
public boolean equals(final Object obj){
    if(obj == this) return true;  // test for reference equality
    if(obj == null) return false; // test for null
    // continue as above

B) selon votre interprétation du contrat égal (), vous pourriez aussi changer la(Les) ligne (s)

    if(obj instanceof Bean){

à

    // make sure you run a null check before this
    if(obj.getClass() == getClass()){ 

si vous utilisez la deuxième version, vous voulez probablement aussi appeler super(equals()) dans votre méthode equals() . Les Opinions diffèrent ici, le sujet est discuté dans cette question:

droit moyen d'intégrer la super-classe dans une Goyave Objets.hashcode() de la mise en œuvre?

(bien qu'il s'agisse d'environ hashCode() , la même chose s'applique à equals() )


Note (inspirée du Commentaire de kayahr )

Objects.hashCode(..) (tout comme le sous-jacent Arrays.hashCode(...) ) pourrait fonctionner mal si vous avez beaucoup de champs primitifs. Dans de tels cas, EqualsBuilder pourrait être la meilleure solution.

206
répondu Sean Patrick Floyd 2018-04-10 19:04:39

Réveillez-vous! depuis Java 7 il y a des méthodes d'aide pour égale et hashCode dans la bibliothèque standard. Leur utilisation est totalement équivalente à l'utilisation de Goyave méthodes.

14
répondu Mikhail Golubtsov 2016-04-04 07:51:38

si vous ne voulez pas dépendre d'une bibliothèque tierce (peut-être que vous utilisez un périphérique avec des ressources limitées) et que vous ne voulez même pas taper vos propres méthodes, vous pouvez aussi laisser L'IDE faire le travail, par exemple dans l'utilisation d'eclipse

Source -> Generate hashCode() and equals()...

vous obtiendrez le code 'natif' que vous pouvez configurer comme vous voulez et que vous devez prise en charge sur les modifications.


exemple (eclipse Juno):

import java.util.Arrays;
import java.util.List;

public class FooBar {

    public String string;
    public List<String> stringList;
    public String[] stringArray;

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((string == null) ? 0 : string.hashCode());
        result = prime * result + Arrays.hashCode(stringArray);
        result = prime * result
                + ((stringList == null) ? 0 : stringList.hashCode());
        return result;
    }
    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        FooBar other = (FooBar) obj;
        if (string == null) {
            if (other.string != null)
                return false;
        } else if (!string.equals(other.string))
            return false;
        if (!Arrays.equals(stringArray, other.stringArray))
            return false;
        if (stringList == null) {
            if (other.stringList != null)
                return false;
        } else if (!stringList.equals(other.stringList))
            return false;
        return true;
    }

}
8
répondu FrVaBe 2015-07-08 20:45:06

La EqualsBuilder et HashCodeBuilder ont deux aspects principaux qui sont différentes de manuellement le code écrit:

  • manutention nulle
  • création d'instance

grâce à EqualsBuilder et HashCodeBuilder, il est plus facile de comparer des champs qui pourraient être nuls. Avec le code écrit manuellement cela crée beaucoup de boilerplate.

le constructeur D'equals va d'autre part créer une instance per égale appel de méthode. Si vos méthodes égales sont souvent appelées, cela créera beaucoup d'instances.

pour hiberner les égaux et l'implémentation du hashCode ne font aucune différence. Ils sont juste un détail d'implémentation. pour presque tous les objets du domaine chargés avec hibernate, le runtime overhead (même sans analyse de fuite) du constructeur peut être ignoré . Les frais généraux de base de données et de communication seront importants.

comme skaffman mentionné la version de réflexion ne peut pas être utilisé dans le code de production. La réflexion sera de ralentir et la "mise en œuvre" ne sera pas correcte pour toutes les classes sauf les plus simples. La prise en compte de tous les membres est également dangereuse, car les membres nouvellement introduits modifient le comportement de la méthode equal. La réflexion peut être utile dans le code de test.

6
répondu Thomas Jung 2011-02-18 08:24:15

si vous ne voulez pas écrire votre propre, Il ya aussi la possibilité d'utiliser google guava (anciennement google collections)

4
répondu Alain Pannetier 2015-11-27 22:21:47

si vous avez juste affaire à l'entité bean où id est une clé primaire, vous pouvez simplifier.

   @Override
   public boolean equals(Object other)
   {
      if (this == other) { return true; }
      if ((other == null) || (other.getClass() != this.getClass())) { return false; }

      EntityBean castOther = (EntityBean) other;
      return new EqualsBuilder().append(this.getId(), castOther.getId()).isEquals();
   }
0
répondu DEREK LEE 2014-01-22 19:30:23

à mon avis, il ne joue pas bien avec Hibernate, en particulier les exemples de la réponse comparant la longueur, le nom et les enfants pour une entité. Hibernate conseille d'utiliser la clé pour être utilisé dans equals() et hashCode(), et ils ont leurs raisons. Si vous utilisez auto equals() et hashCode() générateur sur votre clé d'entreprise, c'est ok, juste des problèmes de performance doivent être considérés comme mentionné précédemment. Mais les gens utilisent généralement toutes les propriétés ce qui est IMO très mauvaise. Par exemple, je travaille actuellement sur un projet où les entités sont écrites en utilisant Pojomatic avec @AutoProperty, ce que je considère comme un très mauvais modèle.

leurs deux principaux scénarios pour utiliser hashCode () et equals () sont:

  • quand vous mettez des instances de classes persistantes dans un ensemble (le
  • lorsque vous utilisez le rattachement d'instances détachées

donc supposons que notre entité ressemble à ceci:

class Entity {
  protected Long id;
  protected String someProp;
  public Entity(Long id, String someProp);
}

Entity entity1 = new Entity(1, "a");
Entity entity2 = new Entity(1, "b");

les deux sont la même entité pour L'hibernation, qui ont été récupérées d'une session à un moment donné (leur id et classe/table sont égaux). Mais quand nous implémentons auto equals () un hashCode () sur tous les accessoires, qu'avons-nous?

  1. lorsque vous mettez l'entity2 à l'ensemble persistant où l'entity1 existe déjà, cela sera mis deux fois et aura pour résultat exception pendant la commit.
  2. si vous voulez attacher l'entité2 détachée à la session, où entity1 existe déjà, ils (probablement, je n'ai pas testé cela en particulier) ne seront pas fusionnés correctement.

ainsi, pour 99% des projets que je réalise, nous utilisons l'implémentation suivante d'equals () et de hashCode () écrits une fois dans la classe de base entity, ce qui est compatible avec les concepts D'hibernation:

@Override
public boolean equals(Object obj) {
    if (StringUtils.isEmpty(id))
        return super.equals(obj);

    return getClass().isInstance(obj) && id.equals(((IDomain) obj).getId());
}

@Override
public int hashCode() {
    return StringUtils.isEmpty(id)
        ? super.hashCode()
        : String.format("%s/%s", getClass().getSimpleName(), getId()).hashCode();
}

pour l'entité transitoire Je fais la même chose que Hibernate fera sur l'étape de persistance, c.-à-d.. J'utilise la correspondance d'instance. Pour les objets persistants je compare la clé unique, qui est la table/id (je n'utilise jamais les clés composites).

0
répondu Łukasz Frankowski 2014-05-22 10:16:09

juste au cas où, d'autres le trouveront utile, je suis venu avec cette classe D'aide pour le calcul de code de hachage qui évite la création d'objet supplémentaire au-dessus mentionné ci-dessus (en fait, la au-dessus des objets.la méthode hash () est encore plus grande quand vous avez l'héritage car elle créera un nouveau tableau à chaque niveau!).

exemple d'utilisation:

public int hashCode() {
    return HashCode.hash(HashCode.hash(timestampMillis), name, dateOfBirth); // timestampMillis is long
}

public int hashCode() {
    return HashCode.hash(super.hashCode(), occupation, children);
}

le helper HashCode:

public class HashCode {

    public static int hash(Object o1, Object o2) {
        return add(Objects.hashCode(o1), o2);
    }

    public static int hash(Object o1, Object o2, Object o3) {
        return hash(Objects.hashCode(o1), o2, o3);
    }

    ...

    public static int hash(Object o1, Object o2, ..., Object o10) {
        return hash(Objects.hashCode(o1), o2, o3, ..., o10);
    }

    public static int hash(int initial, Object o1, Object o2) {
        return add(add(initial, o1), o2);
    }

    ...

    public static int hash(int initial, Object o1, Object o2, ... Object o10) {
        return add(... add(add(add(initial, o1), o2), o3) ..., o10);
    }

    public static int hash(long value) {
        return (int) (value ^ (value >>> 32));
    }

    public static int hash(int initial, long value) {
        return add(initial, hash(value));
    }

    private static int add(int accumulator, Object o) {
        return 31 * accumulator + Objects.hashCode(o);
    }
}

j'ai pensé que 10 est le maximum nombre raisonnable de propriétés dans un modèle de domaine, si vous avez plus que vous devriez penser à refactoring et introduire plus de classe au lieu de maintenir un tas de chaînes et de primitives.

les inconvénients sont: il n'est pas utile si vous avez principalement des primitives et/ou des tableaux que vous devez hachez profondément. (Normalement c'est le cas lorsque vous avez à traiter avec télévision (transfert) des objets qui sont hors de votre contrôle).

0
répondu Vlad 2014-12-22 06:55:06