You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@commons.apache.org by Peter Koßek <Pe...@t-online.de> on 2003/04/23 09:55:27 UTC
Re: [collections] testing equality with some Comparator
Hi Mischa.
I agree to your idea of different functions of equality, so I followed this
discussion with interest. Quite often I have made the experience that
programmers were quarreling about the best equals method for an object, or
worse, one programmer had to work around an inadequate equals method another
programmer had made long before. I think it is better to encapsulate what people
think about equality. E.g., if we have a person object with the variables
'int_id', 'name' and 'address', one user interface programmer will say:
'Equality is the equality of the content, I don't care about that id value'. A
database programmer might say: 'Two objects are equal if they have the same id,
nothing else can be important.' The best way to separate the concerns of these
two people is to create two Person classes for each access level, and to use a
tranformer function, so that each programmer can realize the equals function
he/she dreams about. But we can't always create an own object model for each
application level, can't we? Performance issues and organizational problems
might stop our efforts to build up one level after the other. So it is a good
idea to allow each developer to create an idea of equality for one moment.
I can't solve the problems you realized in your last posting, but I want to add
some thoughts.
1.
> I think you are right with the BinaryPredicate instead of a
> Comparator, since I am only interested in defining an alternative
> equivalence between the elements in the collection, not in some order.
That's right, but the Comparator is the well known mechanism of Java to create
an alternative equality function. People who want to make an equals function
shouldn't have to learn a new class. I would suggest to have an API that easily
allows to use Comparator instances as parameters here.
2.
> The method above as well the alternative implemenation using functors
> don't only require the elements to match, but also to appear in the same
> order. (...) I also want c1 and c2 to be equal to c3 = [(12, alice, another
street),
(11, john, a street)]. This could be solved by sorting, but then we do
> need the Comparator.
In the Java core, java.util.AbstractCollection doesn't define equals, but
AbstractSet and AbstractList do. So it is not easy to be totally compatible to
the core. I would suggest to implement the behavior of AbstractSet.equals, which
says two sets are equal iff they have the same size and each element of one set
is contained in the other. We need a helper method #contains(Collection, Object,
Comparator) or #contains(Collection, Object, BinaryPredicate) here. To be faster
for lists, I wouldn't use the same algorithm to compare two lists. If two lists
are compared, the order of the elements should be considered.
-Peter
RE: [collections] testing equality with some Comparator
Posted by Mischa Barthel <mi...@barthel.com>.
Hi Peter,
Thanks for your reaction. This:
<snip>
> The best way to separate the
> concerns of these
> two people is to create two Person classes for each access
> level, and to use a
> tranformer function, so that each programmer can realize the
> equals function
> he/she dreams about.
<snip>
gave me the following idea. I will not use seperate Person classes but I
will use a transformer to give representatives of Persons. In general an
equivalence on any set also gives a partition of equivalence classes of
this set. These equivalence classes can be represented by a so called
equivalence class representative. I can use a Transformer to specify
these representatives. For my Person example:
public class PersonContentEquivClassRep implements Transformer {
public Object transform(Object obj) {
try {
Person rep = (Person) BeanUtils.cloneBean(obj);
rep.setId(0);
return rep;
} catch (Exception e) {
return null;
}
}
// singleton
...
}
So the method transform(Object obj) gives a representative for the
equivalence classes that I defined earlier with the PersonComparator.
Now I also implement Person.equals() and Person.hashCode() such that
Persons are equal iff all the members are equal:
public int hashCode() {
return (_id + name.hashCode() + address.hashCode());
}
public boolean equals(Object obj) {
if(!(obj instanceof Person)) {
return false;
} else {
Person person = (Person) obj;
return (person.getId() == this._id)
&& person.getName().equals(this.name)
&& person.getAddress().equals(this.address);
}
}
I have implemented PersonContentEquivClassRep in such a way that the
representative is unique with respect to Person.equals(). In general for
such Transformers the following must hold:
1 transform(Object obj) returns a representative of the equivalence
class of which obj is a member, where the alternative equivalence is
used
2 for each two Objects belonging to the same (alternative) equivalence
class transform(Object obj) returns a Object that is unique with respect
to the original equivalence (implemented in the equals method)
3 for each two Objects belonging to different (alternative) equivalence
classes transform(Object obj) returns Objects that are not equal with
respect to the original equivalence (implemented in the equals method)
Note: For these conditions to hold the origininal equivalence must be
stronger than the alternative. Example: If equals() just compared _ids
it would be impossible to implement PersonContentEquivClassRep such that
1-3 would hold, because an integer can not represent two Strings and
for each two combinations of Strings (name and address) a different
integer would be needed to satisfy 2 and 3.
Now I can solve my original problem using
CollectionUtils.collect(Collection, Transformer),
CollectionUtils.isEqualCollection(Collection, Collection) and my
Transformer. This is done as follows (from junit test case):
Person person1 = new Person(1, "John", "Street");
Person person2 = new Person(2, "Alice", "Another street");
Person person3 = new Person(11, "John", "Street");
Person person4 = new Person(12, "Alice", "Another street");
Person person5 = new Person(13, "Bob", "Yet another street");
Collection col1 = new ArrayList();
Collection col2 = new ArrayList();
col1.add(person1);
col1.add(person2);
col2.add(person3);
col2.add(person4);
PersonContentEquivClassRep personContentEquivClassRep =
PersonContentEquivClassRep.getInstance();
Collection col3 = CollectionUtils.collect(col1,
personContentEquivClassRep);
Collection col4 = CollectionUtils.collect(col2,
personContentEquivClassRep);
assertTrue(CollectionUtils.isEqualCollection(col3, col4));
CollectionUtils.isEqualCollection(col3, col4) now does what I originally
wanted: checking equivalence of collections wrt to an alternative
equivalence. This can easily be extended to other methods that use
equivalence. For example: contains:
assertTrue(col3.contains(personContentEquivClassRep.transform(person4)))
;
assertTrue(!col3.contains(personContentEquivClassRep.transform(person5))
);
Greetings,
Mischa.