J'ai un ArrayList
d'objets Test
, qui utilisent une chaîne comme vérification d'équivalence. Je veux pouvoir utiliser List.contains()
pour vérifier si la liste contient ou non un objet qui utilise une certaine chaîne.
Simplement:
Test a = new Test("a");
a.equals("a"); // True
List<Test> test = new ArrayList<Test>();
test.add(a);
test.contains("a"); // False!
Fonction d'égalité et de hachage:
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (o == this) return true;
if (!(o instanceof Test)) {
return (o instanceof String) && (name.equals(o));
}
Test t = (Test)o;
return name.equals(t.GetName());
}
@Override
public int hashCode() {
return name.hashCode();
}
J'ai lu que pour s'assurer que contains
fonctionne pour une classe personnalisée, il doit remplacer equals
. Il est donc très étrange pour moi que tandis que equals
renvoie vrai, contains
renvoie faux.
Comment puis-je faire fonctionner cela?
Le fait que votre Test
equals
puisse renvoyer true lorsque vous lui passez une chaîne ne signifie pas que String
's equals
reviendra toujours vrai lorsque vous passerez un Test
instance. En fait, String
's equals
ne peut renvoyer true
que lorsque l'instance qui lui est passée est un autre String
:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) { // the passed instance must be a String
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
ArrayList
's contains
appelle indexOf
qui utilise la méthode equals
de l'instance recherchée (le String
"a" dans votre exemple), pas le type d'élément de List
(qui est Test
dans votre cas):
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i])) // o in your case is a String while
// elementData[i] is a Test
// so String's equals returns false
return i;
}
return -1;
}
equals()
doit toujours être commutative , c'est-à-dire a.equals(b)
et b.equals(a)
doit toujours renvoyer la même valeur . Ou symétrique, comme le javadoc de equals()
l'appelle:
La méthode
equals
implémente une relation d'équivalence sur les références d'objet non nul:
- C'est reflexive: pour toute valeur de référence non nulle
x
,x.equals(x)
devrait retournertrue
.- C'est symétrique: pour toutes les valeurs de référence non nulles
x
ety
,x.equals(y)
devrait retournertrue
si et seulement siy.equals(x)
renvoietrue
.- C'est transitive: pour toutes les valeurs de référence non nulles
x
,y
etz
, six.equals(y)
renvoietrue
ety.equals(z)
renvoietrue
, puisx.equals(z)
devrait renvoyertrue
.- C'est cohérent: pour toutes les valeurs de référence non nulles
x
ety
, plusieurs invocations dex.equals(y)
retournent systématiquementtrue
ou renvoie systématiquementfalse
, à condition qu'aucune information utilisée dans les comparaisonsequals
sur les objets ne soit modifiée.- Pour toute valeur de référence non nulle
x
,x.equals(null)
doit renvoyerfalse
.
Malheureusement, même la Java Runtime Library se trompe. Date.equals(Timestamp)
comparera les valeurs en millisecondes, ignorant les nanosecondes présentes dans le Timestamp
, tandis que Timestamp.equals(Date)
renvoie false
.
Le problème est que List<E>.contains(object o)
est documenté pour retourner vrai:
si et seulement si cette liste contient au moins un élément e tel que (o == null? e == null: o.equals (e)).
(Depuis https://docs.Oracle.com/javase/8/docs/api/Java/util/List.html#contains-Java.lang.Object- )
Notez qu'il n'effectue pas le test en tant que e.equals(o)
, ce qui serait nécessaire pour que votre test fonctionne. Votre méthode d'égalité ne fonctionne pas de manière commutative ("symétriquement" en utilisant les termes des documents Java).
Documents Java indiquant que la méthode equals()
pour une classe doit suivre ces règles:
La méthode equals implémente une relation d'équivalence sur les références d'objets non nulles:
- Il est réflexif: pour toute valeur de référence non nulle,
x
,x.equals(x)
doit renvoyer true.- Il est symétrique: pour toutes les valeurs de référence non nulles
x
ety
,x.equals(y)
devrait retourner vrai si et seulement siy.equals(x)
renvoie true.- Elle est transitive: pour toutes les valeurs de référence non nulles
x
,y
etz
, six.equals(y)
renvoie true ety.equals(z)
retourne vrai, puisx.equals(z)
devrait retourner vrai.- Il est cohérent: pour toutes les valeurs de référence non nulles
x
ety
, plusieurs invocations dex.equals(y)
retournent systématiquement true ou retournent systématiquement false, à condition qu'aucune information ne soit utilisée dans les comparaisons égales sur les objets est modifié.- Pour toute valeur de référence non nulle,
x
,x.equals(null)
doit renvoyer false.
Si vous écrivez
test.contains(new Test("a"));
alors cela reviendra sûrement vrai. Vous recherchez l'objet chaîne dans la liste de test.