Following code is a wrong implementation of java equals method. It violated the symmetric rule. But I do not know how it violated this rule. Please point it out where in this method it violated the symmetric rule.
public class WrongEquals {
private final String variable;
public WrongEquals(String variable) {
this.variable = variable;
}
#override public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof String) {
return variable.equals((String) o);
}
if (o instanceof WrongEquals) {
return variable.equals(((WrongEquals) o).variable);
}
return false;
}
#override
public int hashCode() {
return (variable == null ? 0 : variable.hashCode());
}
}
Because your WrongEquals instance can be equal to some String, but no String will be equal to any instance of WrongEquals
Look at the String implementation of equals() (as of JDK 7)
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Obviously this implementation ensures that any equality will only exist between another String instance.
(This is just an extension and illustration of Kon's prior answer)
Try adding this main program:
public static void main(String[] args){
WrongEquals a = new WrongEquals("xyzzy");
String b = "xyzzy";
System.out.println(a.equals(b));
System.out.println(b.equals(a));
}
It will display:
true
false
Related
I'm trying to compare two fields (string and integer) using only the Comparable interface. It was my first time using this and I've no idea where to put the second field to compare the values.
public int compareTo(Object o) throws ClassCastException
{
int count = 0;
int compareName = this.lastName.compareTo(((SalePerson) o).getLastName());
int compareSales = Integer.compare(this.totalSales, ((SalePerson) o).getTotalSales());
if(!(o instanceof SalePerson))
{
throw new ClassCastException("A SalePerson object expected.");
}
if((this.totalSales < ((SalePerson) o).getTotalSales()))
{
count = -1;
}
else if((this.totalSales > ((SalePerson) o).getTotalSales()))
{
count = 1;
}
return count;
}
If you want to implement Comparable interface, it is unecassary to throw ClassCastException since o has to be SalePerson, otherwise you will get a compile error.
You can do it this way:
public class SalePerson implements Comparable<SalePerson>{
#Override
public int compareTo(SalePerson o) {
int totalSalesCompare = Integer.compare(this.totalSales, o.getTotalSales());
return totalSalesCompare == 0 ? this.lastName.compareTo(o.getLastName())
: totalSalesCompare;
}
}
Also, the compareTo is suggested to work with equals and hashCode:
#Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof SalePerson)) {
return false;
}
return Integer.compare(Integer.compare(this.totalSales, o.getTotalSales())) == 0
&& this.lastName.equals(o.getLastName());
}
#Override
public int hashCode() {
return this.lastName.hashCode() * 31 + this.totalSales;
}
Class:
public class Variant
{
private String variant;
private String quantity;
//getters and setters
}
ArrayList:
ArrayList<Variant> variantList = getVariantsList();
Now I want to check whether variantList contains a duplicate entry of variant or not? Please note that variant having two entries with different quantity are to be considered as duplicates.
You can simply ovveride your equals method in your Variant class and provide all the rules for equality in that method.
#Override
public boolean equals(Object obj) {
..
Then you can use contains method or just pass it to a Set, that eliminates all your duplicates.
If you want variant having two entries with different quantity also considered as dup, then you can add that condition in your equals.
Override equals(Object obj) method and try to compare the object on variant and quantity.
Try to loop thru the variantList and do check for duplicity using variantList.contains(variant).
There are two things you need to do:
Override the equals() in your Variant class(minimal code below):
Please note that the below code only checks for quantity and not the variant prop. Your IDE might help you to generate the equals() as well.
#Override
public boolean equals(Object object) {
boolean isEqual = (this == object);
if(object instanceof Variant){
Variant variant = (Variant) object;
isEqual = this.quantity.equals(variant.quantity);
}else{
isEqual = false;
}
return isEqual;
}
Check if the List contains the object - which will use the equals() to check if both are equal.
for (Variant variant : variantList) {
if (variantList.contains(variant)) {
//do logic if its present
}
}
Just check one object with other objects of list
Override equals method in Variant class
#Override
public boolean equals(Object obj) {
if (obj != null) {
if (obj instanceof Variant) {
Variant temp = (Variant) obj;
return this.quantity.equals(temp.quantity); //for different quantity
} else {
return false;
}
}
return false;
}
Then check :
for (int i = 0; i < variantList.size(); i++) {
for (int j = 0; j < variantList.size(); j++) {
if (i != j) {
if (iList.get(i).equals(iList.get(j))) {
//logic when duplicate
break;
}
}
}
}
Follow the below guidelines:
Your Class Variant must override the equals method, since you define a duplicate condition based on quality hence in the equals method check for quality attribute value i.e.
public class Variant {
private String variant;
private String quantity;
public Variant(String variant, String quantity) {
this.variant = variant;
this.quantity = quantity;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((quantity == null) ? 0 : quantity.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Variant other = (Variant) obj;
if (quantity == null) {
if (other.quantity != null)
return false;
} else if (!quantity.equals(other.quantity))
return false;
return true;
}
}
Create a method which basically checking whether your list contains the duplicate entries(Variant) or not and return true and false accordingly:
private static boolean isListContainsDuplicateEntries(
ArrayList variantList) {
final List setToReturn = new ArrayList();
for (Variant v : variantList) {
if (!setToReturn.contains(v)) {
setToReturn.add(v);
} else {
return true;
}
}
return false;
}
Now, test the functionality:
public static void main(String[] args) {
Variant variant1 = new Variant("1", "100");
Variant variant2 = new Variant("2", "200");
Variant variant3 = new Variant("3", "200");
ArrayList<Variant> variantList = new ArrayList<>();
variantList.add(variant1);
variantList.add(variant2);
variantList.add(variant3);
System.out.println(Variant.isListContainsDuplicateEntries(variantList));
Output: true
You can use contains():
if (variantList.contains(**<some other Variant object>**)){
...
}
You can simply override your equals method in your Variant and try like this
List<Varient> list =getVariantsList();
System.out.println("here list size"+list.size());
Set<Varient> set = new HashSet<Varient>(list);
System.out.println("here"+set.size());
Create a varient Object:
public class Varient {
private String variant;
private String quantity;
public String getVariant() {
return variant;
}
public void setVariant(String variant) {
this.variant = variant;
}
public String getQuantity() {
return quantity;
}
public void setQuantity(String quantity) {
this.quantity = quantity;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Varient)) return false;
Varient varient = (Varient) o;
if (!quantity.equals(varient.quantity)) return false;
if (!variant.equals(varient.variant)) return false;
return true;
}
#Override
public int hashCode() {
int result = variant.hashCode();
result = 31 * result + quantity.hashCode();
return result;
}
}
Here is your main Program;
public class Test {
public static void main (String [] args){
// getVariantsList() here your list
List<Varient> list =getVariantsList();
Set<Varient> set = new LinkedHashSet<Varient>(list);
}
}
public class Variant {
private String variant;
private String quantity;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((variant == null) ? 0 : variant.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Variant other = (Variant) obj;
if (variant == null) {
if (other.variant != null)
return false;
} else if (!variant.equals(other.variant))
return false;
return true;
}
public String getVariant() {
return variant;
}
public void setVariant(String variant) {
this.variant = variant;
}
public String getQuantity() {
return quantity;
}
public void setQuantity(String quantity) {
this.quantity = quantity;
}
public static void main(String[] args) {
// HashSet<Variant> set = new HashSet<>();
// LinkedHashSet<Variant> linkedSet = new LinkedHashSet<>(); // stores
// in input order
/*
* You can use treeset to store data in custom order, in this case
* lexicographically
*/
TreeSet<Variant> treeSet = new TreeSet<>(new VariantComparator());
}
}
See the following 2 classes, DTO and DTOWithOrdering:
public class DTO {
private final String key;
private final long recordVersionNumber;
public DTO(String key) {
this.key = key;
this.recordVersionNumber = 0;
}
public DTO(String key, long recordVersionNumber) {
this.key = key;
this.recordVersionNumber = recordVersionNumber;
}
public String getKey() {
return key;
}
public long getRecordVersionNumber() {
return recordVersionNumber;
}
#Override
public String toString() {
return "Key: " + this.key + " Record Version Number: " + this.recordVersionNumber;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DTO that = (DTO) o;
return Objects.equal(this.key, that.key) &&
Objects.equal(this.recordVersionNumber, that.recordVersionNumber);
}
#Override
public int hashCode() {
return Objects.hashCode(key, recordVersionNumber);
}
public class DTOWithOrdering extends DTO implements Comparable<DTOWithOrdering> {
public DTOWithOrdering(String key, long recordVersionNumber) {
super(key, recordVersionNumber);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DTOWithOrdering other = (DTOWithOrdering) o;
if(this.getKey().equals(other.getKey())) {
if(this.getRecordVersionNumber() == other.getRecordVersionNumber()) {
return true;
} else if(this.getRecordVersionNumber() <= other.getRecordVersionNumber()) {
return true;
} else {
return false;
}
} {
return false;
}
}
#Override
public int compareTo(DTOWithOrdering other) {
if(this.getKey().equals(other.getKey())) {
if(this.getRecordVersionNumber() == other.getRecordVersionNumber()) {
return 0;
} else if(this.getRecordVersionNumber() <= other.getRecordVersionNumber()) {
return 0;
} else {
return -1;
}
} {
return -1;
}
}
}
DTOWIthOrdering extends from DTO and overrides the equals and compareTo methods.
The problem arises with the following code snippet when I create a TreeSet<DTOWIthOrdering> and invoke contains on this
TreeSet<DTOWithOrdering> treeSet = new TreeSet<DTOWithOrdering>(keyAndVersionList);
List<DTO> results = new ArrayList<DTO>();
for (DTO diff : diffs) {
if (treeSet.contains(new DTOWithOrdering(diff.getKey(), diff.getRecordVersionNumber())) == false) {
results.add(diff);
}
}
When I run this within my program I can see that treeSet contains 2700+ entities, one of which has a key of 0b3ae620-bbcf-347d-a9b4-87e6fd765cd7 and recordVersionNumber of 4
However, one of the diff entities contains the same key with a recordVersionNumber of 0.
When the code invokes the contains method, the set returns a value of false.
Strangely, for other examples, where the keys are equal and the record version number is greater in the TreeSet it returns true!
Is there something wrong here with my logic?
Here is a quote from JavaDoc for Comparable interface:
int compareTo(T o)
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object. The implementor
must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and
y. (This implies that x.compareTo(y) must throw an exception if
y.compareTo(x) throws an exception.)
If you return -1 but never return 1, the property
sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) cannot hold true. So your implementation of this method does not conform to the specification and TreeSet can work improperly.
Can anyone let me know what goes wrong in this piece of code? I'm pulling my hair out!
There isn't any problem if I use HashMap instead of ConcurrentHashMap. The code is compiled with JDK 5.0
public class MapTest {
public Map<DummyKey, DummyValue> testMap = new ConcurrentHashMap<DummyKey, DummyValue>();
public MapTest() {
DummyKey k1 = new DummyKey("A");
DummyValue v1 = new DummyValue("1");
DummyKey k2 = new DummyKey("B");
DummyValue v2 = new DummyValue("2");
testMap.put(k1, v1);
testMap.put(k2, v2);
}
public void printMap() {
for(DummyKey key : testMap.keySet()){
System.out.println(key.getKeyName());
DummyValue val = testMap.get(key);
System.out.println(val.getValue());
}
}
public static void main(String[] args){
MapTest main = new MapTest();
main.printMap();
}
private static class DummyKey {
private String keyName = "";
public DummyKey(String keyName){
this.keyName = keyName;
}
public String getKeyName() {
return keyName;
}
#Override
public int hashCode() {
return keyName.hashCode();
}
#Override
public boolean equals(Object o) {
return keyName.equals(o);
}
}
private static class DummyValue {
private String value = "";
public DummyValue(String value){
this.value = value;
}
public String getValue() {
return value;
}
}
}
This is the output:
B
Exception in thread "main" java.lang.NullPointerException
at test.MapTest.printMap(MapTest.java:27)
at test.MapTest.main(MapTest.java:34)
DummyKey.equals method implementation is incorrect, due to that testMap.get(key) always returns null. Try this
public boolean equals(Object o) {
if (o instanceof DummyKey) {
DummyKey other = (DummyKey) o;
return keyName == null ? other.keyName == null : keyName.equals(other.keyName);
}
return false;
}
hashCode also needs a little change to be consistent with equals
public int hashCode() {
return keyName == null ? 0 : keyName.hashCode();
}
The problem comes from your equals in DummyKey.
When you call DummyValue val = testMap.get(key);, the hashcode function finds a match (both keyname of k1 and key are the same and so are their hashcode). Yet equals returns false because k1.keyname is equal to "A" which is not equal to key itself, which is actually of type DummyValue: you are not comparing properly!
Therefore, you need to modify your equals function:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DummyKey other = (DummyKey) obj;
if (keyName == null) {
if (other.keyName != null)
return false;
} else if (!keyName.equals(other.keyName))
return false;
return true;
}
Please note that if you change hashCode(), then you must change equals() as well. Otherwise, you will run into problems. If equals() returns true for two items, then their hashCode() value must be equal! The opposite is not required but preferable for better hashing performance. Here is an implementation of equals() and hashCode().
HINT: if you are using eclipse, you can utilize its source generation capability to create the correct hashCode() and equals() method for you. The only thing you need to do is to pick the instance variables that identify the object. To do so in eclipse, while your source code is open, go to the tabs in the top and choose "source", then choose "Generate hashCode() and equals()..."
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((keyName == null) ? 0 : keyName.hashCode());
return result;
}
Override
public boolean equals(Object other) {
if(this == other) return true; //for optimization
if(! other instanceof this) return false; //also covers for when other == null
return this.keyName == null ? other.keyName == null : this.keyName.equals(other.keyName);
}
As others have pointed, the problem lies in the way you override hashcode and equals.
Two options : 1) Just remove the hashcode and equals and it works fine
2) I let eclipse generate the source for hashcode and equals and it works fine. This is what my eclipse belted out for me :
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((keyName == null) ? 0 : keyName.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DummyKey other = (DummyKey) obj;
if (keyName == null) {
if (other.keyName != null)
return false;
} else if (!keyName.equals(other.keyName))
return false;
return true;
}
I have a class called MyClass:
public class MyClass extends abstractClass implements
someInterface {
Set<VNode> relation_;
Set<VNode> x_;
Set<VNode> y_;
#Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
return false;
}
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof MyClass)) {
return false;
}
MyClass other = (MyClass) obj;
if (relation_ == null) {
if (other.relation_ != null) {
return false;
}
} else if (!relation_.equals(other.relation_)) {
return false;
}
if (x_ == null) {
if (other.x_ != null) {
return false;
}
} else if (!x_.equals(other.x_)) {
return false;
}
if (y_ == null) {
if (other.y_ != null) {
return false;
}
} else if (!y_.equals(other.y_)) {
return false;
}
return true;
}
#Override
public int hashCode() {
int res = new HashCodeBuilder(17, 37).append(relation_).append(x_)
.append(y_).append(getWeight()).toHashCode();
return res;
}
}
The abstract class is as follows:
public abstract class abstractClass {
double weight_;
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof abstractClass)) {
return false;
}
abstractClass other = (abstractClass) obj;
if (Double.doubleToLongBits(weight_) != Double
.doubleToLongBits(other.weight_)) {
return false;
}
return true;
}
public double getWeight() {
return weight_;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(weight_);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
Now, if I have HashSet<MyClass> s1 and an MyClass i1, even if s1 has an element s1i whith s1i.equals(i1)=true and s1i.hashCode()=i1.hashCode(), s1.contains(i1) gives me false.
Any explanations?
Other classes:
public class VNode {
Mention mention_;
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof VNode)) {
return false;
}
VNode other = (VNode) obj;
if (mention_ == null) {
if (other.mention_ != null) {
return false;
}
} else if (!mention_.equals(other.mention_)) {
return false;
}
return true;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((mention_ == null) ? 0 : mention_.hashCode());
return result;
}
}
public class Mention extends Range {
private final int id_;
public Mention(final int start, final int end) {
super(start, end);
id_ = getNextMentionID();
}
}
public class Range {
private final int start_;
private final int end_;
/**
* Contr.
*
* #param start
* #param end
*/
public Range(final int start, final int end) {
start_ = start;
end_ = end;
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Range)) {
return false;
}
Range other = (Range) obj;
if (end_ != other.end_) {
return false;
}
if (start_ != other.start_) {
return false;
}
return true;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + end_;
result = prime * result + start_;
return result;
}
}
Your equals() method is not readable at all. Since you are using HashCodeBuilder in hashCode(), why not use EqualsBuilder as well?
Version a)
public boolean equals(Object obj){
if(obj == null || obj.getClass()!=getClass()){
return false;
}
MyClass other = (MyClass) obj;
return new EqualsBuilder()
// check parent properties first
.append(this.getWeight(), other.getWeight())
.append(this.relation_, other.relation_)
.append(this.x_, other.x_)
.append(this.y_, other.y_)
.isEquals();
}
Version b)
public boolean equals(Object obj){
// delegate to parent equals first
if(!super.equals(obj)){
return false;
}
MyClass other = (MyClass) obj;
return new EqualsBuilder()
.append(this.relation_, other.relation_)
.append(this.x_, other.x_)
.append(this.y_, other.y_)
.isEquals();
}
Each class should only be concerned with its own variables when calculating equals and hashcode. So, in your MyClass instead of calling getWeight() you should be using the hashcode of the super class. Like you are with equals()!. In this case the effect will be the same.
public int hashCode() {
int res = new HashCodeBuilder(super.hashcode(), 37).append(relation_).append(x_)
.append(y_);
return res;
}
This means any changes to the base class that may affect equals and hashcode are confined to the class and you don't have to update the sub classes.
(Not really an answer, more an observation but its too big for a comment)