hashCode dans TopiaEntityAbstract
Salut les lutins, Dans TopiaEntityAbstract, le code de la méthode hashCode me parrait un peu étrange. /** * On utilise la date de creation comme hash code, cette date ne varie pas * au cours du temps */ @Override public int hashCode() { int result = getTopiaCreateDate().hashCode(); return result; } Cela transgresse l'ordre naturel des entités. On devrait toujours avoir a.equals(b) ==> a.hashCode() == b.hashCode() J'ai un validateur dans Jaxx qui utilise les hashCodes de proprietes d'une entite pour construire une empreinte de clef unique (et manque de bol une des propriété est une autre entités,...); vu le code dans TopiaEntityAbstract ça ne peut pas marcher. Dans la javadoc de Sun, il est préciser que modifier l'idiom entre la méthode equals et hashCode doit être justifié. Je n'ai pas vu de doc là-dessus dans ToPIA :) Y'a-t-il une raison précise pour utiliser la date de creation comme hashCode ? Est-ce qu'on pourrait changer ça en deportant le calcul du hashCode sur celui du topiaId : @Override public int hashCode() { int hash = 97 * 7; if (topiaId != null) { hash += topiaId.hashCode(); } return hash; } Tony
On Mon, 4 May 2009 05:15:10 +0200 Tony Chemit <chemit@codelutin.com> wrote:
Salut les lutins,
Dans TopiaEntityAbstract, le code de la méthode hashCode me parrait un peu étrange.
/** * On utilise la date de creation comme hash code, cette date ne varie pas * au cours du temps */ @Override public int hashCode() { int result = getTopiaCreateDate().hashCode(); return result; }
Cela transgresse l'ordre naturel des entités. On devrait toujours avoir
a.equals(b) ==> a.hashCode() == b.hashCode()
Oui, et alors c bien le cas ? si a.equals(b) ca veut dire qu'ils ont aussi la meme date de creation. Ou alors ca veut dire que le equals est mal code, mais pas le hashcode
Est-ce qu'on pourrait changer ça en deportant le calcul du hashCode sur celui du topiaId :
@Override public int hashCode() { int hash = 97 * 7; if (topiaId != null) { hash += topiaId.hashCode(); } return hash; }
Il me semble que ca changera rien. Car de toute facon la date de creation est un sous ensemble du topiaId: topiaId => class#date de creation#random et surtout le hashcode doit etre constant dans le temps, et il me semble que le topiaId, risque de passer de null a quelque chose alors que la date de creation doit toujours etre valide. A verifier tout de meme, mais je pense que ton probleme est ailleurs :) -- Benjamin -------------------- tél: +33 (0) 2 40 50 29 28 email: poussin@codelutin.com () campagne du ruban ascii http://www.codelutin.com /\ pour les mails en ascii
Le Mon, 4 May 2009 09:48:22 +0200, Benjamin POUSSIN <poussin@codelutin.com> a écrit :
On Mon, 4 May 2009 05:15:10 +0200 Tony Chemit <chemit@codelutin.com> wrote:
Salut les lutins,
Dans TopiaEntityAbstract, le code de la méthode hashCode me parrait un peu étrange.
/** * On utilise la date de creation comme hash code, cette date ne varie pas * au cours du temps */ @Override public int hashCode() { int result = getTopiaCreateDate().hashCode(); return result; }
Cela transgresse l'ordre naturel des entités. On devrait toujours avoir
a.equals(b) ==> a.hashCode() == b.hashCode()
Oui, et alors c bien le cas ? si a.equals(b) ca veut dire qu'ils ont aussi la meme date de creation. Ou alors ca veut dire que le equals est mal code, mais pas le hashcode
Est-ce qu'on pourrait changer ça en deportant le calcul du hashCode sur celui du topiaId :
@Override public int hashCode() { int hash = 97 * 7; if (topiaId != null) { hash += topiaId.hashCode(); } return hash; }
Il me semble que ca changera rien. Car de toute facon la date de creation est un sous ensemble du topiaId: topiaId => class#date de creation#random
et surtout le hashcode doit etre constant dans le temps, et il me semble que le topiaId, risque de passer de null a quelque chose alors que la date de creation doit toujours etre valide.
Moi ce qui me dérange un peu c'est que le hashCode n'est pas assez fin par rapport au equals. Par contre sur le fait du caractère immuable du hashCode dans le temps, je suis pas trop d'accord, car je pensais qu'un hashCode pouvais varier au cours du temps, dans la javadoc de Object#hashCode, il est bien indiquer que la valeur de la méthode peut varier au cours du temps mais pas au sein de l'execution d'une même application
A verifier tout de meme, mais je pense que ton probleme est ailleurs :)
Ce que je voulais faire c'est pouvoir tester l'égalité d'une clef sur une liste d'entité, pour ce faire je fais ça : boolean answer = true; Set<Integer> hashCodes = new java.util.HashSet<Integer>(); for (Object entry : col) { // construction du hash de la clef d'unicite Integer hash = getUniqueKeyHashCode(entry); if (!hashCodes.contains(hash)) { hashCodes.add(hash); continue; } // une entree avec ce hash a deja ete trouvee // on est donc en violation sur la clef unique answer = false; } et la methode getUniqueKeyHashCode : protected Integer getUniqueKeyHashCode(Object o) { // calcul du hash à la volée HashCodeBuilder builder = new HashCodeBuilder(); for (String key : this.keys) { Object property = getFieldValue(key, o); builder.append(property); } return builder.toHashCode(); } Dans la javadoc de la mé&thode hashCode, y'a écrit : As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. C'est en fait une best-pratice mais c'est pas obligatoire :) et je m'étais basé dessus. Quelqu'un a une idée pour que je réalise un code qui fonctionne tout le temps ?
-- Benjamin -------------------- tél: +33 (0) 2 40 50 29 28 email: poussin@codelutin.com () campagne du ruban ascii http://www.codelutin.com /\ pour les mails en ascii _______________________________________________ Topia-devel mailing list Topia-devel@lists.labs.libre-entreprise.org http://lists.labs.libre-entreprise.org/mailman/listinfo/topia-devel
Le Mon, 4 May 2009 16:12:10 +0200, Tony Chemit <chemit@codelutin.com> a écrit :
Le Mon, 4 May 2009 09:48:22 +0200, Benjamin POUSSIN <poussin@codelutin.com> a écrit :
On Mon, 4 May 2009 05:15:10 +0200 Tony Chemit <chemit@codelutin.com> wrote:
Salut les lutins,
Dans TopiaEntityAbstract, le code de la méthode hashCode me parrait un peu étrange.
/** * On utilise la date de creation comme hash code, cette date ne varie pas * au cours du temps */ @Override public int hashCode() { int result = getTopiaCreateDate().hashCode(); return result; }
Cela transgresse l'ordre naturel des entités. On devrait toujours avoir
a.equals(b) ==> a.hashCode() == b.hashCode()
Oui, et alors c bien le cas ? si a.equals(b) ca veut dire qu'ils ont aussi la meme date de creation. Ou alors ca veut dire que le equals est mal code, mais pas le hashcode
Est-ce qu'on pourrait changer ça en deportant le calcul du hashCode sur celui du topiaId :
@Override public int hashCode() { int hash = 97 * 7; if (topiaId != null) { hash += topiaId.hashCode(); } return hash; }
Il me semble que ca changera rien. Car de toute facon la date de creation est un sous ensemble du topiaId: topiaId => class#date de creation#random
et surtout le hashcode doit etre constant dans le temps, et il me semble que le topiaId, risque de passer de null a quelque chose alors que la date de creation doit toujours etre valide.
Moi ce qui me dérange un peu c'est que le hashCode n'est pas assez fin par rapport au equals.
Par contre sur le fait du caractère immuable du hashCode dans le temps, je suis pas trop d'accord, car je pensais qu'un hashCode pouvais varier au cours du temps, dans la javadoc de Object#hashCode, il est bien indiquer que la valeur de la méthode peut varier au cours du temps mais pas au sein de l'execution d'une même application
A verifier tout de meme, mais je pense que ton probleme est ailleurs :)
Ce que je voulais faire c'est pouvoir tester l'égalité d'une clef sur une liste d'entité, pour ce faire je fais ça :
boolean answer = true;
Set<Integer> hashCodes = new java.util.HashSet<Integer>(); for (Object entry : col) { // construction du hash de la clef d'unicite Integer hash = getUniqueKeyHashCode(entry); if (!hashCodes.contains(hash)) { hashCodes.add(hash); continue; } // une entree avec ce hash a deja ete trouvee // on est donc en violation sur la clef unique answer = false; }
et la methode getUniqueKeyHashCode :
protected Integer getUniqueKeyHashCode(Object o) { // calcul du hash à la volée HashCodeBuilder builder = new HashCodeBuilder(); for (String key : this.keys) { Object property = getFieldValue(key, o); builder.append(property); } return builder.toHashCode(); }
Dans la javadoc de la mé&thode hashCode, y'a écrit :
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.
C'est en fait une best-pratice mais c'est pas obligatoire :) et je m'étais basé dessus.
Quelqu'un a une idée pour que je réalise un code qui fonctionne tout le temps ?
Moi :) les clef uniques pour mes validateurs ne seront plus basé directement sur les associations mais sur les topia ids des associations ;) et là tout fonctionnera bien
-- Benjamin -------------------- tél: +33 (0) 2 40 50 29 28 email: poussin@codelutin.com () campagne du ruban ascii http://www.codelutin.com /\ pour les mails en ascii _______________________________________________ Topia-devel mailing list Topia-devel@lists.labs.libre-entreprise.org http://lists.labs.libre-entreprise.org/mailman/listinfo/topia-devel
_______________________________________________ Topia-devel mailing list Topia-devel@lists.labs.libre-entreprise.org http://lists.labs.libre-entreprise.org/mailman/listinfo/topia-devel
Tony Chemit a écrit :
Moi :) les clef uniques pour mes validateurs ne seront plus basé directement sur les associations mais sur les topia ids des associations ;) et là tout fonctionnera bien
Moi j'aurai été assez pour baser le hashCode sur le topiaId. Si je reprends l'idée de Benjamin, c'est qu'il y a un laps de temps entre la création de l'objet et l'assignation de son topiaId, du coup calcul du hashCode avec : impossible ? Quel risque on a ? Si un objet est pas trouvé la première fois il va parcourir toute la liste, mais normalement, on assigne tout de suite un topiaId à une entité ? Tony, j'ai du mal à comprendre ton histoire de uniqueKeyHashCode, surtout au niveau du rôle de "this.keys". Mais d'après ca : getFieldValue(key, o); si on change une valeur de l'entité, le hashCode change ? Sinon pour le fait qu'un hashCode doit être immuable dans un runtime d'une appli, pour moi le fait qu'il soit immuable sur toute sa vie, c'est pas plus mal, dans le sens que : "qui peut le plus peut le moins" ? Arno. -- Société Code Lutin http://www.codelutin.com tel : 02 40 50 29 28 fax : 09 59 92 29 28
On Mon, 04 May 2009 16:50:50 +0200 Arnaud Thimel <thimel@codelutin.com> wrote:
Tony Chemit a écrit :
Moi :) les clef uniques pour mes validateurs ne seront plus basé directement sur les associations mais sur les topia ids des associations ;) et là tout fonctionnera bien
Moi j'aurai été assez pour baser le hashCode sur le topiaId. Si je reprends l'idée de Benjamin, c'est qu'il y a un laps de temps entre la création de l'objet et l'assignation de son topiaId, du coup calcul du hashCode avec : impossible ? Quel risque on a ? Si un objet est pas trouvé la première fois il va parcourir toute la liste, mais normalement, on assigne tout de suite un topiaId à une entité ?
Oui, je ne suis pas contre, mais si ca pas ete fait comme ca c qu'il y a une raison :). Mais je ne me rappelle plus exactement laquel :(. Il me semble que lors de la creation d'une entity, chez nous ca poserait pas de probleme (c justement ce que je disais qu'il fallait verifier). Mais le but etait aussi que topia puisse fonctionner avec des ID qui ne serait pas des topia ID, et donc assigne par exemple lors de la sauvegarde en base. Et du coupe le code suivant fait un magnique memory leak :(: entity = dao.create(); hashset.add(entity); ... entity.save(); ... hashset.remove(entity); car entre le add et le remove le hash a change car l'id a change car assigne au moment du save. Et donc lorsqu'on fait le remove le Set ne retrouve pas l'entite car la premiere recherche se fait sur le hash. rem: avec un Vector/ArrayList/... ca n'arriverait pas :) Et donc la seul chose vraiment stable est la date de creation de l'entite, qui est defini des la creation et qui ne varie pas. De plus elle est presque unique donc pas mauvaise (date de creation en milliseconde, y'a pas des tas d'objet cree en 1ms :)
Tony, j'ai du mal à comprendre ton histoire de uniqueKeyHashCode, surtout au niveau du rôle de "this.keys".
oui, pareil :)
Mais d'après ca : getFieldValue(key, o); si on change une valeur de l'entité, le hashCode change ?
ce qui est tres, tres mauvais :(
Sinon pour le fait qu'un hashCode doit être immuable dans un runtime d'une appli, pour moi le fait qu'il soit immuable sur toute sa vie, c'est pas plus mal, dans le sens que : "qui peut le plus peut le moins" ?
Le immutable dans le runtime du au fait explique plus haut. Mais je trouve que c encore mieux s'il ne varie jamais. Car par exemple si on serialise une HashMap ou un HashSet. Et que les hash changes entre deux execution, la serialisation ne sert strictement a rien, car aucun objet ne pourra etre retrouve :( Dans ton cas, ou en fait du ne souhaite pas vraiment utilise le hash mais avec quelque chose d'unique, le topiaId est la bonne solution :). Et il faut bien toujours avoir en tete que A !=B n'implique pas A.hash != B.hash. -- Benjamin -------------------- tél: +33 (0) 2 40 50 29 28 email: poussin@codelutin.com () campagne du ruban ascii http://www.codelutin.com /\ pour les mails en ascii
Le Mon, 4 May 2009 17:11:46 +0200, Benjamin POUSSIN <poussin@codelutin.com> a écrit :
Tony, j'ai du mal à comprendre ton histoire de uniqueKeyHashCode, surtout au niveau du rôle de "this.keys".
oui, pareil :)
Sorry, je me suis mal exprimé, je recommence : Soit un objet A. On définit sur A une clef d'unicité K(A) = [p0(A),p1(A),...], où pi(A) est une propriété de A. (c'est une clef métier) On veut un algorithme pour vérifier que tous les objets d'une collection de A ont une clef K(A) différente. Dans mon code : this.keys = K(A) Pour chaque objet je calcule le hashCode correspondant à la méthode equals de la clef d'unicité. Je regarde ensuite si j'ai déjà rencontré ce hash : - si oui c'est que la clef a déjà été rencontrée, je peux quitter - sinon je conserve le hashCode et passe à l'élément suivant Mais vu qu'il n'y a pas d'équivalence entre o.equals(o1) et o.hashCode()== o1.hashCode(), donc on peut avoir un même hashCode sur mes clefs alors que les clefs sont différentes... Conclusion : cet algorithme fonctionne très bien si l'équivalence est respectée entre hashCode et equals et on peut l'utiliser si on utilise par exemple uniquement des types primitifs qui respecte la convention. Je vais mettre un restriction sur mon validateur pour qu'il ne fonctionne que sur des types primitifs, cela suffit à mon besoin pour le moment... C'est un peu plus clair ? :)
Tony Chemit a écrit :
Sorry, je me suis mal exprimé, je recommence :
Soit un objet A.
On définit sur A une clef d'unicité K(A) = [p0(A),p1(A),...], où pi(A) est une propriété de A. (c'est une clef métier)
Ok, mais là ta clé varie si tes champs varient ? (ou alors j'ai encore pas compris et là t'auras le droit d'me taper ;)) -- Société Code Lutin http://www.codelutin.com tel : 02 40 50 29 28 fax : 09 59 92 29 28
Le Mon, 04 May 2009 18:47:16 +0200, Arnaud Thimel <thimel@codelutin.com> a écrit :
Tony Chemit a écrit :
Sorry, je me suis mal exprimé, je recommence :
Soit un objet A.
On définit sur A une clef d'unicité K(A) = [p0(A),p1(A),...], où pi(A) est une propriété de A. (c'est une clef métier)
Ok, mais là ta clé varie si tes champs varient ? (ou alors j'ai encore pas compris et là t'auras le droit d'me taper ;))
oui la clef varie :) t'as tout compris A chaque validation, on reconstruit les clefs (euh les hashCodes des clefs :)).
Tony Chemit a écrit :
oui la clef varie :) t'as tout compris
A chaque validation, on reconstruit les clefs (euh les hashCodes des clefs :)).
Ok. Et c'est pas grave que les hashCode des clés varient ? Arno. -- Société Code Lutin http://www.codelutin.com tel : 02 40 50 29 28 fax : 09 59 92 29 28
On Mon, 4 May 2009 20:09:40 +0200 Tony Chemit <chemit@codelutin.com> wrote:
Le Mon, 04 May 2009 18:47:16 +0200, Arnaud Thimel <thimel@codelutin.com> a écrit :
Tony Chemit a écrit :
Sorry, je me suis mal exprimé, je recommence :
Soit un objet A.
On définit sur A une clef d'unicité K(A) = [p0(A),p1(A),...], où pi(A) est une propriété de A. (c'est une clef métier)
Ok, mais là ta clé varie si tes champs varient ? (ou alors j'ai encore pas compris et là t'auras le droit d'me taper ;))
oui la clef varie :) t'as tout compris
A chaque validation, on reconstruit les clefs (euh les hashCodes des clefs :)).
J'ai du mal a comprendre le besoin :(, car je pense que ce que tu fais ne marche pas mais comme je ne sais pas a quoi il sert je ne peux pas te donner de bonne solution :(. car par exemple si tu prends comme type primitif String (ce qu'il n'est pas) tu n'auras jamais la certitude que deux chaines differentes n'ont pas le hashCode. Si dans les types primitifs tu prends bien que les primitifs alors oui le hash de chacun d'eux devrait normalement etre different, mais en les composants tu ne peux plus etre sur que le hash soit different. Juste pour illustrer, meme si ce n'est pas exactement ca: 1+3 = 4 :) 2+2 = 4 :( -- Benjamin -------------------- tél: +33 (0) 2 40 50 29 28 email: poussin@codelutin.com () campagne du ruban ascii http://www.codelutin.com /\ pour les mails en ascii
Le Tue, 5 May 2009 17:33:27 +0200, Benjamin POUSSIN <poussin@codelutin.com> a écrit :
On Mon, 4 May 2009 20:09:40 +0200 Tony Chemit <chemit@codelutin.com> wrote:
Le Mon, 04 May 2009 18:47:16 +0200, Arnaud Thimel <thimel@codelutin.com> a écrit :
Tony Chemit a écrit :
Sorry, je me suis mal exprimé, je recommence :
Soit un objet A.
On définit sur A une clef d'unicité K(A) = [p0(A),p1(A),...], où pi(A) est une propriété de A. (c'est une clef métier)
Ok, mais là ta clé varie si tes champs varient ? (ou alors j'ai encore pas compris et là t'auras le droit d'me taper ;))
oui la clef varie :) t'as tout compris
A chaque validation, on reconstruit les clefs (euh les hashCodes des clefs :)).
J'ai du mal a comprendre le besoin :(, car je pense que ce que tu fais ne marche pas mais comme je ne sais pas a quoi il sert je ne peux pas te donner de bonne solution :(.
car par exemple si tu prends comme type primitif String (ce qu'il n'est pas) tu n'auras jamais la certitude que deux chaines differentes n'ont pas le hashCode.
Si dans les types primitifs tu prends bien que les primitifs alors oui le hash de chacun d'eux devrait normalement etre different, mais en les composants tu ne peux plus etre sur que le hash soit different.
Juste pour illustrer, meme si ce n'est pas exactement ca: 1+3 = 4 :) 2+2 = 4 :(
Oui mais en utilisant un hashCodeBuilder la composition fonctionne encore :) (initialiseur : un entier premier, et un multiplicatuer premier aussi).
-- Benjamin -------------------- tél: +33 (0) 2 40 50 29 28 email: poussin@codelutin.com () campagne du ruban ascii http://www.codelutin.com /\ pour les mails en ascii _______________________________________________ Topia-devel mailing list Topia-devel@lists.labs.libre-entreprise.org http://lists.labs.libre-entreprise.org/mailman/listinfo/topia-devel
participants (3)
-
Arnaud Thimel -
Benjamin POUSSIN -
Tony Chemit