Конечно, вы можете создать некоторый внешний объект, обеспечивающий сравнение на равенство и HashCode.Но встроенные коллекции Java не используют такой объект для их сравнения / поиска.
Однажды я создал такой интерфейс в моей коллекции пакетов (только что переведенный на английский):
public interface HashableEquivalenceRelation {
/**
* Returns true if two objects are considered equal.
*
* This should form an equivalence relation, meaning it
* should fulfill these properties:
* <ul>
* <li>Reflexivity: {@code areEqual(o, o)}
* should always return true.</li>
* <li>Symmetry: {@code areEqual(o1,o2) == areEqual(o2,o1)}
* for all objects o1 and o2</li>
* <li>Transitivity: If {@code areEqual(o1, o2)} and {@code areEqual(o2,o3)},
* then {@code areEqual(o1,o3}} should hold too.</li>
* </ul>
* Additionally, the relation should be temporary consistent, i.e. the
* result of this method for the same two objects should not change as
* long as the objects do not change significantly (the precise meaning of
* <em>change significantly</em> is dependent on the implementation).
*
* Also, if {@code areEqual(o1, o2)} holds true, then {@code hashCode(o1) == hashCode(o2)}
* must be true too.
*/
public boolean areEqual(Object o1, Object o2);
/**
* Returns a hashCode for an arbitrary object.
*
* This should be temporary consistent, i.e. the result for the same
* objects should not change as long as the object does not change significantly
* (with change significantly having the same meaning as for {@link areEqual}).
*
* Also, if {@code areEqual(o1, o2)} holds true, then {@code hashCode(o1) == hashCode(o2)}
* must be true too.
*/
public int hashCode(Object o);
}
Чем у меня была группа интерфейсов CustomCollection
, CustomSet
, CustomList
, CustomMap
и т. Д., Определенные как интерфейсы в java.util
, но с использованием такого отношения эквивалентности для всех методоввместо встроенного отношения, заданного Object.equals
.У меня тоже было несколько реализаций по умолчанию:
/**
* The equivalence relation induced by Object#equals.
*/
public final static EquivalenceRelation DEFAULT =
new EquivalenceRelation() {
public boolean areEqual(Object o1, Object o2)
{
return
o1 == o2 ||
o1 != null &&
o1.equals(o2);
}
public int hashCode(Object ob)
{
return
ob == null?
0 :
ob.hashCode();
}
public String toString() { return "<DEFAULT>"; }
};
/**
* The equivalence relation induced by {@code ==}.
* (The hashCode used is {@link System#identityHashCode}.)
*/
public final static EquivalenceRelation IDENTITY =
new EquivalenceRelation() {
public boolean areEqual(Object o1, Object o2) { return o1 == o2; }
public int hashCode(Object ob) { return System.identityHashCode(ob); }
public String toString() { return "<IDENTITY>"; }
};
/**
* The all-relation: every object is equivalent to every other one.
*/
public final static EquivalenceRelation ALL =
new EquivalenceRelation() {
public boolean areEqual(Object o1, Object o2) { return true; }
public int hashCode(Object ob) { return 0; }
public String toString() { return "<ALL>"; }
};
/**
* An equivalence relation partitioning the references
* in two groups: the null reference and any other reference.
*/
public final static EquivalenceRelation NULL_OR_NOT_NULL =
new EquivalenceRelation() {
public boolean areEqual(Object o1, Object o2)
{
return (o1 == null && o2 == null) ||
(o1 != null && o2 != null);
}
public int hashCode(Object o) { return o == null ? 0 : 1; }
public String toString() { return "<NULL_OR_NOT_NULL>"; }
};
/**
* Two objects are equivalent if they are of the same (actual) class.
*/
public final static EquivalenceRelation SAME_CLASS =
new EquivalenceRelation() {
public boolean areEqual(Object o1, Object o2)
{
return o1 == o2 || o1 != null && o2 != null &&
o1.getClass() == o2.getClass();
}
public int hashCode(Object o) { return o == null ? 0 : o.getClass().hashCode(); }
public String toString() { return "<SAME_CLASS>"; }
};
/**
* Compares strings ignoring case.
* Other objects give a {@link ClassCastException}.
*/
public final static EquivalenceRelation STRINGS_IGNORE_CASE =
new EquivalenceRelation() {
public boolean areEqual(Object o1, Object o2)
{
return o1 == null ?
o2 == null :
((String)o1).equalsIgnoreCase((String)o2);
}
public int hashCode(Object o)
{
return o == null ? -12345 : ((String)o).toUpperCase().hashCode();
}
public String toString() { return "<STRINGS_IGNORE_CASE>"; }
};
/**
* Compares {@link CharSequence} implementations by content.
* Other object give a {@link ClassCastException}.
*/
public final static EquivalenceRelation CHAR_SEQUENCE_CONTENT =
new EquivalenceRelation() {
public boolean areEqual(Object o1, Object o2)
{
CharSequence seq1 = (CharSequence)o1;
CharSequence seq2 = (CharSequence)o2;
if (seq1 == null ^ seq2 == null) // nur eins von beiden null
return false;
if (seq1 == seq2) // umfasst auch den Fall null == null
return true;
int size = seq1.length();
if (seq2.length() != size)
return false;
for (int i = 0; i < size; i++)
{
if (seq1.charAt(i) != seq2.charAt(i))
return false;
}
return true;
}
/**
* Entrspricht String.hashCode
*/
public int hashCode(Object o)
{
CharSequence sequence = (CharSequence)o;
if (sequence == null)
return 0;
int hash = 0;
int size = sequence.length();
for (int i = 0; i < size; i++)
{
hash = hash * 31 + sequence.charAt(i);
}
return hash;
}
};