Your methods compareTo, equals and hashCode are violating their general contracts yes. Let’s look at them starting with compareTo:
Method compareTo
@Override
public int compareTo(Pai o){
if (this != null && o != null)
return (this.ordem - o.ordem);
else{
return 0;
}
}
The expression this != null is always true, because there is no possible way for the object this be it null. So it boils down to this:
@Override
public int compareTo(Pai o) {
if (o != null) return this.ordem - o.ordem;
return 0;
}
Note the return 0. It is only possible if the value of the o for null. In the meantime, this is what interface javadoc Comparable:
Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException
What translating into Portuguese is:
Note that null is not an instance of any class, and that e.compareTo(null) should launch a NullPointerException
I mean, you can simplify your compareTo to look like this:
@Override
public int compareTo(Pai o) {
return this.ordem - o.ordem;
}
Method equals
Now let’s see the equals:
@Override
public boolean equals(Object o){
if (o != null && o instanceof Pai && this != null){
return (this.indice == ((Pai) o).indice);
}
else {
return false;
}
}
The understatement this != null is always true, and therefore can be eliminated (there is no way this was null). Moreover, if o != null is fake, so o instanceof Pai will also be false, and therefore the o != null is unnecessary and redundant. Therefore, your method equals comes down to this:
@Override
public boolean equals(Object o) {
if (o instanceof Pai) {
return this.indice == ((Pai) o).indice;
} else {
return false;
}
}
Which in turn can be simplified in this:
@Override
public boolean equals(Object o) {
return o instanceof Pai && this.indice == ((Pai) o).indice;
}
However, I notice that the field indice is the type Integer, and not int. Compare two objects of the type Integer using the operator == is as problematic as comparing Strings using the ==, and can result in unpleasant surprises. For example:
Integer a = new Integer(777);
Integer b = new Integer(777);
System.out.println(a == b); // Escreve "false".
So, that makes yours equals:
@Override
public boolean equals(Object o) {
return o instanceof Pai && Objects.equals(this.indice, ((Pai) o).indice);
}
There is one more but... The class Pai has subclasses. And it’s never a good idea to have objects from different classes that can be equal. Like this method equals will be inherited by all subclasses, we will have it possible that two objects of different classes (but both subclasses of Pai) would be considered equal by having the same values in the field indice. Therefore, this would be a better implementation for your equals:
@Override
public boolean equals(Object o) {
return o != null
&& o.getClass() == this.getClass()
&& Objects.equals(this.indice, ((Pai) o).indice);
}
Method hashCode
And now your method hashCode. Well... You did not implement the hashCode! It happens that it is fundamental, because the equals as described in javadoc of the method equals(Object):
Note that it is generally necessary to override the hashCode method Whenever this method is overridden, so as to maintain the general Contract for the hashCode method, which States that Equal Objects must have Equal hash codes.
What translating into Portuguese is:
Note that it is usually necessary to overwrite the method hashCode where this method is superscripted in order to maintain the general contract for the method hashCode, which specifies that equal objects should have equal hash codes.
And looking at the javadoc of the method hashCode():
If two Objects are Equal According to the equals(Object) method, then Calling the hashCode method on each of the two Objects must Produce the same integer result.
What translating into Portuguese is:
If two objects are equal according to the method equals(Object), then call the method hashCode in each of these two objects must produce the same entire result.
A very simple implementation for your method hashCode that works in conjunction with the equals and with the compareTo that would be it:
@Override
public int hashCode() {
return indice == null ? 0 : indice;
}
compareTo consistent with equals
Finally, it is expected that a and b are two equal elements (ie a.equals(b) is true), so it is expected that a.compareTo(b) is zero. However, this does not occur in your code. Although the Comparator Allow this, he strongly recommends against:
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.
What translating into Portuguese:
The natural ordination of a class C is said to be consistent with the equals if and only if e1.compareTo(e2) == 0 has the same Boolean value as e1.equals(e2) for each e1 and e2 class C.
[...]
It is strongly recommended (though not required) that natural ordinations be consistent with the equals.
Once you order using the field ordem and checks equality using the field indice, then its ordering is not consistent with the equals.
I note that in your code, you never seem to set a value for the field indice, and so I think what you really wanted was to check equality through the field ordem. If so, then you would erase the field indice and these would be his equals and its hashCode:
@Override
public boolean equals(Object o) {
return o != null
&& o.getClass() == this.getClass()
&& Objects.equals(this.ordem, ((Pai) o).ordem);
}
@Override
public int hashCode() {
return ordem == null ? 0 : ordem;
}
If you really want to use the indice for some purpose other than ordering, so I recommend that you define that two objects are equal when the fields indice and ordem are equal, and therefore their equals and hashCode would look like this:
@Override
public boolean equals(Object o) {
return o != null
&& o.getClass() == this.getClass()
&& Objects.equals(this.indice, ((Pai) o).indice)
&& Objects.equals(this.ordem, ((Pai) o).ordem);
}
@Override
public int hashCode() {
return Objects.hash(indice, ordem);
}
There’s one more problem, your compareTo will give a NullPointerException unwanted when the field ordem of any of the objects is null.
However, you may prefer to put the fields ordem and indice as int, and in this case, although you can replace the use of Objects.equals of his method equals back to the ==, it will continue to work if you use Objects.equals.
With all this, you’ll leave yours compareTo consistent with the equals and will not break the contract of any of the methods compareTo, equals or hashCode.
It may have to do with competition or not. From what is said in the OS in English the immediate cause seems to be that the
Comparatoris not being fulfilled (an example is when it does not satisfy the transitivity property). According to this answer the methodArrays.sort()which is used byCollections.sort()changed the implementation in Java 7 and now launches aIllegalArgumentExceptionif it detects that aComparatorviolated the contract expected of him.– Piovezan
I have doubts that only use the
synchronizedList()solve the problem. Thesort()works by moving elements from the place list and although these insertion and removal operations are now atomic, you can enter between them during thesortan element insertion made by your code. Ideally access to the entire list during the entire operation ofsort()is atomic (caught the difference?).– Piovezan
Relevant, but from what you describe, it’s not that: http://answall.com/a/44045/132
– Victor Stafusa