Can we change behavior of equals method dynamically? - java

I have one class having two variables named as x and y. In this class I have overrided the equals and hashCode methods to compare two object of this class. But our requirement is to compare two object of this class sometimes on the basis of x and sometimes on the basis of y. Is it possible dynamically in Java?
Edit:
I have one more class named as B, in this class there is two method m1 and m2 and I want to compare the above class object in such a way that when we call from m1 (for sorting) the above objects will be compared on the basis of x (means compare object by compare x variable) and when we call from m2 (for sorting) then we compare according to y.

Changing behavior based on last method to call your method is possible, but you shouldn't do it for a lot of reasons.
it violates the equals contract, thus breaking the functionality of several algorithms designed to handle collections
result of the comparison cannot be anymore known without knowing the caller, which is a hard dependency that's prone to break
However, if you insist you need it, you can do like
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
if (stackTraceElements.length < 3)
{
// do something when last method to call is not available
// probably you'll want to return something
}
String callerMethod = stackTraceElements[2].getMethodName();
if (callerMethod.equals("m1"))
{
// something
} else
{
// something else
}
This example is simplified as it assumes the method calling this method is the candidate - it can be some method further down the call stack.
As noted, this is not recommended. Rather use different kind of comparators for the purpose, and give a relevant comparator to the sort method to have different kind of sorting per context.

Depending on the complexity of the comparison, you can either do this within the class or use two seperate comparator classes.
public boolean equals(Object other){
if(condition == true){
return x==x;
}else{
return y==y;
}
}
or
public boolean equals(Object other){
if(condition == true){
return new CompareX(this, other).compare();
}else{
return new CompareY(this, other).compare();
}
}
You have to extend the comparison logic to a valid one, of course.
Oh and, the same principle applies to the hashCode.

It's not possible, to change the behaviour of equals dynamically. You have to use Comparator to provide the comparison from the outside of the class.
Since Java8 with Lambdas it is easy to use Comparators.
There is a method comparing. You can create Comparators out of Methods, which you want to compare.
// A comparator comparing on x
Comparator<A> comp1 = comparing (a -> a.x);
// A comparator comparing on the output of m1
Comparator<A> comp2 = comparing (A::m1);
// A comparator comparing on the output of m1 and when equals, comparing on x
Comparator<A> comp2 = comparing (A::m1).thenComparing (a -> a.x);
From the external point you can decide, which comparator to use.
There's a new way to sort your data in Java8, too:
List<A> data;
data.stream ().sorted (comparing (a -> a.x));
Of course you have to be allowed to use Java8 for this.

If you can add flag setting code to m1 and m2 you can modify eis answer to get rid of the kludgy stacktrace stuff.
It is still kludgy.

Related

Why do two different HashSets with the same data have the same HashCode?

I recently ran across a problem on leetcode which I solved with a nested hashset. This is the problem, if you're interested: https://leetcode.com/problems/group-anagrams/.
My intuition was to add all of the letters of each word into a hashset, then put that hashset into another hashset. At each iteration, I would check if the hashset already existed, and if it did, add to the existing hashset.
Oddly enough, that seems to work. Why do 2 hashsets share the same hashcode if they are different objects? Would something like if(set1.hashCode() == set2.hashCode()) doStuff() be valid code?
This is expected. HashSet extends AbstractSet. The hashCode() method in AbstractSet says:
Returns the hash code value for this set. The hash code of a set is defined to be the sum of the hash codes of the elements in the set, where the hash code of a null element is defined to be zero. This ensures that s1.equals(s2) implies that s1.hashCode()==s2.hashCode() for any two sets s1 and s2, as required by the general contract of Object.hashCode.
This implementation iterates over the set, calling the hashCode method on each element in the set, and adding up the results.
Here's the code from AbstractSet:
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
Why do 2 hashsets share the same hashcode if they are different objects?
With HashSet, the hashCode is calculated using the contents of the set. Since it's just numeric addition, the order of addition doesn't matter – just add them all up. So it makes sense that you have two sets, each containing objects which are equivalent (and thus should have matching hashCode() values), and then the sum of hashCodes within each set is the same.
Would something like if(set1.hashCode() == set2.hashCode()) doStuff() be valid code?
Sure.
EDIT: The best way of comparing two sets for equality is to use equals(). In the case of AbstractSet, calling set1.equals(set2) would result in individual calls to equals() at the level of the objects within the set (as well as some other checks).
Why do two different HashSets with the same data have the same
HashCode?
Actually this is needed to fulfill another need that is specified in Java.
The equals method of Set is overridden to take in consideration that equals returns true (example a.equals(b)) if:
a is of type Set and b is of type Set.
both a and b have exactly the same size.
a contains all elements of b.
b contains all elements of a.
Since the default equals (which compares only the memory reference to be the same) is overridden for Set, according to java guidelines the hashCode method has to be overridden as well. So, this custom implementation of hashCode is provided in order to match with the custom implementation of equals.
In order to see why it is necessary to override hashCode method when the equals method is overridden, you can take a look at this previous answer of mine.
Why do 2 hashsets share the same hashcode if they are different
objects
Because as explained above this is needed so that Set can have the custom functionality for equals that it currently has.
If you want to just check if a and b are different instances of set you can still check this with operators == and !=.
a == b -> true means a and b point to the same instance of Set in memory
a != b -> true means a and b point to different instances of Set in memory

Is there an alternative to NavigableSet when a "Set" is not appropriate?

The NavigableSet interface offers a number of useful methods that a normal Set does not (specifically I'm thinking about methods like headSet and tailSet for instance). However, being a Set, it does not support duplicate elements. Also, being a SortedSet, the ordering must be consistent with equals and hashCode to avoid violating the contract of the Set interface.
Is there any good alternative data structure for when there might be duplicate elements or multiple elements that are "equal" according to the natural ordering or Comparator but not "equal" according to the equals method? As a motivating example, consider the following code that shows why a NavigableSet is not appropriate:
public class Foo implements Comparable<Foo>{
double x;
double y;
#Override
public int compareTo(Foo o) {
return Double.compare(x, o.x); // only x matters for sort order
}
public static void main(String...args){
Foo a = new Foo();
a.x = 1;
a.y = 2;
Foo b = new Foo();
b.x = 1;
b.y = 42;
Foo c = new Foo();
c.x = 2;
c.y = 12.34;
NavigableSet<Foo> set = new TreeSet<Foo>();
set.add(a);
set.add(a);
set.add(b);
set.add(c);
System.out.println(set.size());
}
}
Notice that element a only gets added once (of course, since this is a Set). Also, notice that b does not get added, since there is already an element for which the comparison returns 0.
I felt like this was probably a fairly common thing, so I hoped to find an existing implementation rather than rolling my own. Is there a good, widely-used data structure for my purposes?
I'll add that while writing this question I did come across the Biscotti Project, but a) I'm not convinced it solves the comparison/equals issue and b) the FAQ explicitly says it's not really safe to use.
Let me reformulate your question to make sure I understand it well.
The need for headSet and tailSet implies the collection has to be sorted. Which is kind of in conflict with the need for allowing the duplicate members according to compareTo.
The conflict comes from the effective usage of such collection. Adding a member to sorted collection is done utilizing the compareTo method in O(log n) - kind of binary search and then add. The TreeSet is implemented using TreeMap which can't two same members according to compareTo.
What you are looking for won't be effective.
You may try to use a simple ArrayList and sort it by Collections.sort and then use sublist method. The problem with this is that it don't deal with duplicates at all.
You may also use the LinkedHashSet which deals with duplicates (according to equals() and it is immune to compareTo()), but it is not sorted. Of course, you may convert the LinkedHashSet instance to the SortedSet by passing its instance in constructor.

A Mechanism for having different equals (physical equals and logical equals) on objects in Collection

Is there any Equalator mechanism like Comparator so I can have different equals for coparing lists?
EDIT: My goal is to differentiate between current list1.equals(list2) which checks if its a shallow copy or also a deep copy with all objects a.equals(b) and list1.identical(list2) which checks if its simply shallow copy with unmodified listing
All these lists are from the same model. Some are copies of themselves so they hold the pointer to same objects, and others are deep copies so hierarchy is totally replicated, because they have updates in content, not just in structure.
I find myself oftenly makin list1.equals(list2) but I need a mechanism for telling if both are TOTAL copies (same objects in same order for collections) or sometimes if they are LOGICAL copies (through my own implemented logic equals), so list would call equals and objects should implement something more than a==b.
My problem is there is no Equalator interface, and if I override objects equals to I loose the capability of comparing by TOTAL EQUAL (a==b)
For example, this would be nice;
Collections.equal(l1,l2,new Equalator(){
#Override public boolean equals(Obj1,Obj2){
//Default lists comparison plus commparison of objects based on
return (obj1.propertyX() == obj2.propertyX());
}
});
and still I could do list1.equals(list2) so they use default equals (obj1==obj2) and this would be only true if contained objects are exactly the same.
First operation is useful for checking if list (which could be an updated list with totally recreated objects from the model) is still equals to the old list.
Second operation is useful for checking if list (which was a shallow copy of the old current version of data model), it does not contain any transcendent change from moving it around inside the code when it was the udpdated version.
EDIT: A very good example would be having a list of Point(x,y). We should be able to know if both list are equal because they are exactly same set of points or equal because the points they contain are equal in a logical way. If we could implement both phyEqual and logEqual to object, and have both methods in any object so list.phyEqual(list2) or list1.logEqual(list2)
Your question doesn't really come across clearly, at least to me. If this doesn't answer it properly, could you re-word a little bit?
Within a given Collection concrete type, most equals implementations already do what you hint at.
For instance:
public boolean equals(Object o) {
if (o == this)
return true;
In this case, something like this might make sense.
You can easily override this:
#Override
public boolean equals(Object o) {
if (o.getPrimaryKey() == this.getPrimaryKey())
return true;
return super().equals(o);
[test for nulls should be added]
If you are creating standard collections, you can even anonymously override the equals method during construction.
If this doesn't do what you want, you could extend Collections yourself and override any of the methods there to do a similar thing.
Does that help?
A late answer, but maybe it will be useful for someone...
The Guava Equivalence class is the same for equivalence as Comparator for comparing. You would need to write your own method for comparing the lists (there is no support in Guava for that), but then you could call this method with various equivalence-definitions.
Or you can roll your own interface:
interface Equalator<T> {
boolean equals(T o1, T o2);
}
Again, you need to write your (trivial) method
boolean <T> listEquals(List<T> list1, List<T> list2, Equalator<T> equalator) {
...
}
...but then you can reuse it with different ListEqualator implementations.

I was advised not to use reflection here... Why not?

This code is used to sort a List. The list could contain in the thousands of elements but less than 10k.
protected <E> int compareFields(E o1, E o2, String fieldName){
try {
Comparable o1Data = (Comparable) o1.getClass().getMethod(fieldName).invoke(o1);
Comparable o2Data = (Comparable) o2.getClass().getMethod(fieldName).invoke(o2);
return o1Data == null ? o2Data == null ? 0 : 1 :
o2Data == null ? -1 : o1Data.compareTo(o2Data);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
I was advised to
"Please don't use reflection for things like this!!
Either supply the method with a suitable Comparator, or a method to extract the relevant property (may be computed in a way not supported by the original type), or both."
An example of a better way to do this would be nice.
Context:
I've got many screens with data tables. Each one is build from a List. Each data table needs to be sortable by each of its 6 columns. The columns are either Date or String.
Using reflection here will potentially be much slower, as you are adding number of stack frames to each comparison by using getClass, getMethod and invoke rather than using the objects' native compare method.
Ideally, you would write the method to avoid the use of object in the signature. A "suitable Comparator" would at least be strongly bound to the objects' type (which you assume are the same). If you must have dynamic field comparison (as it appears), then at least the reflection could be encapsulated in that comparator.
If you are going to call this thousands of times, though, it would be best to pre-bind a Comparator to the field you are sorting by. This way, you only call getMethod once up front, rather than once for each individual comparison.
It's hard to give a good example without context, so for now here's a small list of why it's not the best idea:
The field provided has no guarantee of being Comparable (not sure why the code here needs to catch that exception and rebrand it).
What if the type of objects provided are not meant to be compared in this way? (It's an overly generic method name to know how it's supposed to be used).
It's not strongly typed. Providing the field name as a string means you'll have to change your code everywhere whenever the property name changes, and it'll be hard to track down where you need to make those changes.
Reflection is potentially slower than if implemented in a strongly typed manner.
The other answers described well why it is not advised to use reflection. I want to add an example using a more conventional solution.
Instead of specifying the field that is used to compare the two objects, you should take a Comparator instance as an argument. This way the client who uses this method can specify how to compare the two objects.
protected <E> int compareFields(E o1, E o2, Comparator<E> comparator) {
return comparator.compare(o1, o2);
}
And an example call to this function would look like this:
MyClass a = ...;
MyClass b = ...;
Comparator<MyClass> intFieldComparator = new Comparator<MyClass> {
public int compare(MyClass o1, MyClass o2) {
int field1 = o1.getIntField();
int field2 = o2.getIntField();
return field2 - field1;
}
};
compareFields(a, b, intFieldComparator);
You can define different comparators if you want to compare the objects using several fields.

Java Set collection - override equals method

Is there any way to override the the equals method used by a Set datatype? I wrote a custom equals method for a class called Fee. Now I have a LnkedList of Fee and I want to ensure that there are no duplicated entries. Thus I am considering using a Set insted of a LinkedList, but the criteria for deciding if two fees are equal resides in the overriden equals method in the Fee class.
If using a LinkedList, I will have to iterate over every list item and call the overriden equals method in the Fee class with the remaining entries as a parameter. Just reading this alone sounds like too much processing and will add to computational complexity.
Can I use Set with an overridden equals method? Should I?
As Jeff Foster said:
The Set.equals() method is only used to compare two sets for equality.
You can use a Set to get rid of the duplicate entries, but beware: HashSet doesn't use the equals() methods of its containing objects to determine equality.
A HashSet carries an internal HashMap with <Integer(HashCode), Object> entries and uses equals() as well as the equals method of the HashCode to determine equality.
One way to solve the issue is to override hashCode() in the Class that you put in the Set, so that it represents your equals() criteria
For Example:
class Fee {
String name;
public boolean equals(Object o) {
return (o instanceof Fee) && ((Fee)o.getName()).equals(this.getName());
}
public int hashCode() {
return name.hashCode();
}
}
You can and should use a Set to hold an object type with an overridden equals method, but you may need to override hashCode() too. Equal objects must have equal hash codes.
For example:
public Fee{
public String fi;
public String fo;
public int hashCode(){
return fi.hashCode() ^ fo.hashCode();
}
public boolean equals(Object obj){
return fi.equals(obj.fi) && fo.equals(obj.fo);
}
}
(With null checks as necessary, of course.)
Sets often use hashCode() to optimize performance, and will misbehave if your hashCode method is broken. For example, HashSet uses an internal HashMap.
If you check the source code of HashMap, you'll see it depends on both the hashCode() and the equals() methods of the elements to determine equality:
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
If the hash is not generated correctly, your equals method may never get called.
To make your set faster, you should generate distinct hash codes for objects that are not equal, wherever possible.
Set uses the equals method of the object added to the set. The JavaDoc states
A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.
The Set.equals() method is only used to compare two sets for equality. It's never used as part of adding/remove items from the set.
One solution would be to use a TreeSet with a Comparator.
From the documentation:
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.
This approach would be much faster than using a LinkedList, but a bit slower than a HashSet (ln(n) vs n).
It's worth noting a one side effect of using TreeSet would be that your set is sorted.
There are PredicatedList or PredicatedSet in Apache Commons Collection

Categories