Can we override the intersection method of Guava to compare objects? - java

I have tried to compare to sets of type String using the Sets.intersection method() in Guava. It works fine. I want to know what method should I implement to compare two objects? I have overridden the compareTo() method but the Sets.intersection treat similar objects as different. Can you advice please?
Thanks.

Given the implementation of Sets.intersection():
public static <E> SetView<E> intersection(final Set<E> set1, final Set<?> set2) {
//...
return new SetView<E>() {
//...
#Override public boolean contains(Object object) {
return set1.contains(object) && set2.contains(object);
}
#Override public boolean containsAll(Collection<?> collection) {
return set1.containsAll(collection)
&& set2.containsAll(collection);
}
};
}
I'd say you have to implement whatever methods are needed to make contains() and containsAll() work for the Sets you pass in, because all the work is delegated to the Sets you pass in.
So for HashSets that would be equals() and hashCode(), and for TreeSets that would be compareTo() if you implement Comparable or compare() if you use a Comparator (probably want to still override equals() and hashCode() to keep things consistent outside the Map too).

You just have to read the definition of Set:
More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.
Implement hashCode() and equals().

Related

Adding Object to HashSet

I am trying to add an Object (Exception) to a Set, however it adds every Exception, eventhough some are duplicates.
debug
In my case duplicates are Exceptions which have the same Detail message.
How do I properly add the Exceptions to the HashSet only if the Exception.getDetails() doesn't already exist?
Is there another approach than a HashSet?
Performance is a criteria here, quadratic solutions (O(n^2))are not an option.
You have a few options:
override hashcode and equals in your exception class
use a TreeSet with a custom Comparator
use a Map<String, Exception> where the key is the getDetails() result (for example, a HashMap)
You need to override how the Execptions are compared so it recognises duplicates the way you want. You can't do this for a HashSet but you can for TreeSet e.g.
Set<Exception> exceptions = new TreeSet<>(Comparator.comparing(Object::toString));
This example compares the toString which is the exception type and message in most cases.
If you really want to use a HashSet you need to wrap the Exception in a class which implements hashCode and equals the way you want.
If all you care about is the type and message you can store just the toString of each exception
final Set<String> exceptions = new HashSet<>();
public void addException(Exception e) {
exceptions.add(e.toString());
}
You need to redefine equals and hashCode methods.
If the detail is a String you can redefine them as follow
public boolean equals(Object obj) {
if (!(obj instanceof YourException)) {
return false;
}
return getDetail().equals(((YourException) obj).getDetail());
}
public int hashCode() {
return getDetail().hashCode();
}
Consider this code as a base to program. You have to check for null values for example.
Once redefined equals and hashCode inserting YourException in a TreeSet is an operation done in O(log(n)) where n is the size of the set, from javadoc:
This implementation provides guaranteed log(n) time cost for the basic operations (add, remove and contains).

Comparator interface's equals method, why it is always safe not to override Object.equals(Object)

I'm currently studying the Comparator interface and noticed that in the documentation for Comparator's equals method, it states
Note that it is always safe not to override Object.equals(Object)
I have checked the implementation for the default equals method in Object class
So with the default implementation of equals method, it simply checks whether two instances points to the same object because this == obj tests for reference equality.
But what happen if I have two instances of Comparator, the result they return are identical, and I want to know if they are equivalent. If I dont override the default Object's equals method, then regardless of whether the result they return is equivalent or not, by using the default Object's equals method, false will always be returned. So is it still always safe not to override the Object.equals(Object)?
I suppose you mis-interpreted what java doc is saying:
this method can return true only if the specified object is also a comparator and it imposes the same ordering as this comparator
Default implementation will return true only if it is exactly the same object, which by definition means that they both (actually single one) provide the same sorting order
This definition does not imply that it will return true for different comparators even if they provide the same sorting order, simple verification:
import java.util.Comparator;
public class TestComparator {
static class Comparator1 implements Comparator<Integer> {
#Override
public int compare(final Integer o1, final Integer o2) {
return Integer.compare(o1, o2);
}
}
static class Comparator2 implements Comparator<Integer> {
#Override
public int compare(final Integer o1, final Integer o2) {
return Integer.compare(o1, o2);
}
}
public static void main(final String[] args) {
final Comparator1 c1 = new Comparator1();
final Comparator1 c11 = new Comparator1();
final Comparator2 c2 = new Comparator2();
System.out.println(c1.equals(c1)); // true
System.out.println(c1.equals(c11)); // false
System.out.println(c1.equals(c2)); // false
}
}
Default implementation can return true if comparators equivalent, also it can return false (if objects are different)
Note further, doc says:
However, overriding this method may, in some cases, improve performance by allowing programs to determine that two distinct comparators impose the same order.
So, it is safe not to override, but it is not enough to guarantee that different but equivalent comparators will be compared properly

How to sort a list of class objects on one of its fields?

I have a list of DataPoint objects. The class definition is:
public static class DataPoint
{
public Comparable X;
public Comparable Y;
public Comparable Z;
public String text;
...
}
"list" is an ArrayList of DataPoint objects. How do I sort list only on the X value? Would Collections.sort(list, comparator) be used here?
Yes, you should create specific comparator for each field. Example:
Comparator<DataPoint> compByX = new Comparator<DataPoint>() {
#Override
public int compare(DataPoint left, DataPoint right) {
return left.X.compareTo(right.X);
}
};
Collections.sort(list, compByX);
You have two choices:
implement Comparable<DataPoint> for your DataPoint class
write a custom comparator that implements Comparator<DataPoint> and then use Collections.sort
First solution is meaningful if you want to give a natural ordering on your objects (which will be the most used one). Usually it's the one you use first while you use comparators just when you need additional orderings.
They both behave in the same way but Comparable<T> is inherently attached to the object as it is its default comparison algorithm. Whenever sorting is involved the default one will be used unless you specify another one.
class DataPoint implements Comparable<DataPoint> {
#Override
public int compareTo(DataPoint o) {
return X.compareTo(o.X);
}
}
Mind that when you need to compare objects you usually need also other operations on them so take care of overriding hashCode() and equals(Object o). The latter is used in sorting as documentations states:
The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. ... It is strongly recommended (though not required) that natural orderings be consistent with equals.
This means that if you just compare X variable then two different DataPoint objects with same X will be considered equal with respect to compareTo. This can lead to strange situations.

Possible bug in java.util.Set API [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
java.util.Set API states:
sets contain no pair of elements e1 and e2 such that e1.equals(e2)
But as far as I understand TreeSet uses Comparable/Comparator to determine if e1 and e2 are duplicates. Am I missing something?
If compareTo is consistent with equals (which it should), it doesn't matter whether a TreeSet uses compareTo or equals to determine equality. From the JavaDoc:
Note that the ordering maintained by a set (whether or not an explicit
comparator is provided) must be consistent with equals if it is to
correctly implement the Set interface. (See Comparable or Comparator
for a precise definition of consistent with equals.) This is so
because the Set interface is defined in terms of the equals operation,
but a TreeSet instance performs all element comparisons using its
compareTo (or compare) method, so two elements that are deemed equal
by this method are, from the standpoint of the set, equal. The
behavior of a set is well-defined even if its ordering is inconsistent
with equals; it just fails to obey the general contract of the Set
interface.
This program will print 'true', even though equals always returns false. But the bug would be in the fact the compareTo and equals are inconsistent for A, and is not a bug in TreeSet.
class A implements Comparable<A> {
public int compareTo(A a) {
return 0;
}
public boolean equals(Object other) {
return false;
}
public static void main(String[] args) {
TreeSet<A> set = new TreeSet<A>();
set.add(new A());
System.out.println(set.contains(new A()));
}
}
TreeSet use Comparator. Do you know it actually maintains elements by using map.
public TreeSet() {
this(new TreeMap<E,Object>());
}
As you said Sets contain no pair of elements e1 and e2 such that e1.equals(e2). That is why Comparator has equals method.
No matter how we are going to use Set it depends with Comparator. If we use our own comparator, we can use Set as List but normally we don't do that. If we add our own Object in to TreeSet must pass Comparator when initializing TreeSet or object must be implemented by Comparable
Comparable
A comparable object having capability to compare itself with another object
Comparator Used for comparing two different objects.
public int compare(Employee o1, Employee o2) {
// if sort by name
return o1.getName().compareTo(o2.getName());
}
public boolean equals(Object obj) {
// equals with ID
Employee e = (Employee)obj;
return this.getId().equals(e.getId());
}
Here is Java Sorting: Comparator vs Comparable a tutorial.
Your assumption is wrong; TreeSet doesn't use Comparable/Comparator to determine if e1 is equal to e2.
Equals method is part of Object class and used to decide if two elements of collection are same or not. i.e. e1.equals(e2)
But Comparable/Comparator interface is used to decide if an element is greater than, equal or less than other element. And this is used during sorting. So only thing you need to ensure is that equals and compareto methods are consistent.
So to compare two objects equals method is used and comparator/comparable is used during sorting
EDIT
Below is the method definition from JDK 6; equals method of AbstractSet. TreeSet extends AbstractSet
public boolean equals(Object obj)
{
if(obj == this)
return true;
if(!(obj instanceof Set))
return false;
Collection collection = (Collection)obj;
if(collection.size() != size())
return false;
try
{
return containsAll(collection);
}
catch(ClassCastException classcastexception)
{
return false;
}
catch(NullPointerException nullpointerexception)
{
return false;
}
}
http://docs.oracle.com/javase/6/docs/api/java/util/AbstractSet.html

How to compare Classes and Inherited Classes in Java

I have two classes - Task (which implements Comparable) and DeadlinedTask (where DeadlinedTask extends Task). And for each of them I have written an overloaded compareTo function (each has compareTo(Task) and compareTo(DeadlinedTask)).
The idea is that I can sort normal Tasks by category, and DeadlinedTasks by deadline, but I also want all of the DeadlinedTasks to be sorted above the Tasks.
When I call Collections.sort(myListOfTasks) on a list of only Tasks (no DeadlinedTasks), everything works like a charm.
However when I have a list of both Tasks and DeadlinedTasks, the objects change order, but they are not fully sorted.
I have tried returning numbers other than 1 on the interclass compares (1, 1000, 1000000 all did the same thing). Is there any way to do this through compareTo and Collections.sort, is there a different java functionality I can use, or do I have to write my own search function (as a Comparator?)?
Task compareTo Methods:
public int compareTo(Task other){
if(this.GetCategory().compareTo(other.GetCategory())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetCategory().compareTo(other.GetCategory());
}
public int compareTo(DeadlinedTask other){
return 1;
}
DeadlinedTask compareTo Methods:
public int compareTo(Task other){
return -1;
}
public int compareTo(DeadlinedTask other){
if(this.GetDeadline().compareTo(other.GetDeadline())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetDeadline().compareTo(other.GetDeadline());
}
Thanks for any help
... or do I have to write my own search function (as a Comparator?)?
Yes. I think that's the best way.
The normal way to handle equals and compareTo is to return false (for equals) or throw ClassCastException (for compareTo) if the arguments actual type doesn't match the actual type of this.
If you try to implement equals or compareTo for subtypes, you can easily create semantic anomalies such as:
a.equals(b) and b.equals(a) returning different values, or
a.compareTo(b) and b.compareTo(a) returning inconsistent values.
Avoiding those anomalies would entail making the supertype aware of the subtype. That is a bad from a design perspective because it restricts your ability to create more subtypes in the future.
For use-cases where you need to implement a rule that orders instances of two or more different classes, a Comparator is the best solution.
Per class, only one compareTo method can be used to implement the Comparable interface. If you use Comparable without generics, then this is
public int compareTo(Object o)
If you're using generics, e.g. Comparable<Task>, then it's
public int compareTo(Task o)
Your compareTo(DeadlinedTask o) method will be ignored concerning the Comparable<Task> interface. It just "accidentally" has the same name, but it's an independent overloading.
(By the way, it's not possible to implement both Comparable<Task> and Comparable<DeadlineTask>).
So what you'll have to do instead, is change your Task.compareTo(Task o) method to use instanceof (it has to use runtime information after all). I agree with Stephen, that it would even be better to write a Comparator.
Comparable defines a natural order for all instances of a class. So if DeadlinedTask should always come before Tasks, then the compareTo method should implement it.
You should not redefine compareTo in DeadlinedTask, because this would break the contract of anti-commutativity : if (t1.compareTo(t2) > 0), then t2.compareTo(t1) < 0.
I would thus completely avoid to implement Comparable in the Task class, and use a dedicated comparator when sorting a collection of tasks. If you really want your task to implement Comparable, than you need to make its implementation depend on the existence of DeadlinedTask (which is not very OO) :
public class Task implements Comparable<Task> {
// ...
public final int compareTo(Task t) {
if (this instanceof DeadlinedTask) {
if (t instanceof DeadlinedTask) {
return ((DeadlinedTask) this).getDeadline().compareTo(((DeadlinedTask) t).getDeadline());
}
else {
return -1;
}
}
else if (t instanceof DeadlinedTask) {
return 1;
}
else {
return this.category.compareTo(t.category);
}
}
}
Note that Java uses a lower-case letter at the beginning of methods (getDeadline(), and not GetDeadline()), and that you don't need to use getters to access private properties of your own class.
In addition to what StevenC have said, if you know in advance that you will have a hierarchy of value objects, you can check whether the class of the argument of the compareTo() method is a subtype of the class of the object and if yes, reverse the comparison, so you will always have the child comparing against the parent:
public boolean compareTo(Object o) {
// check for null
boolean isSubtype = getClass().isAssignableFrom(o.getClass()) && getClass()!=o.getClass()
if (isSubtype) return -((/*cast to this type*/) o).compareTo(this);
}
This way, the comparison remains consistent and the base type does not to be aware pf each individual subtype, but just that subtypes exist.
Yes it seems a comparator is the simplest (& cleanest way)
But you can simply delegate te bulk ot the work to the compareTo(...) methods you have already written, all you really need to add is code to handle comparison between the sub and super classes:
public int Compare(Task t1, Task t2) {
if (t1 instance of DeadlinedTask && !(t2 instanceof DeadlinedTask))
return 1;
else if (t2 instance of DeadlinedTask && !(t1 instanceof DeadlinedTask))
return -1;
else
return t1.compareTo(t2);
}
but it just occured, how are you declaring the classes? do you include Comparable in the implements clause of the Task class and visa versa? if not, then perhaps when the lhs object is a Task, then only compare(Task) gets called ?? otherwise you need to have both in the implements clause ie:
class Task implements Comparable<Task>, Comparable<DeadlinedTask>
The magnitude of the value returned will not change anything, ie returning 1 and 1000000 is exactly the same, as tests are only < 0, > 0 and == 0 (this contract IS specified in the docs for the Comparator interface. I used to tell students trying to remember what return values mean, to imagine comparing ints, then we could just write:
int compare (int a, int b) { return a - b; }

Categories

Resources