Why Java TreeMap not printing all its inserted entries - java

Pls tell what is wrong is happening here. I have a Person class which I'm using as a key in TreeMap. I have implemented Comparable also so that TreeMap can do sorting.
public class Person implements Comparable{
private String name;
private int age;
// getters and setters were omitted
#Override
public int compareTo(Object o) {
return 0;
}
}
Now I created TreeMap and added values in it as:
Map treeMap=new TreeMap<Person,Object>();
treeMap.put(new Person(), "String1");
treeMap.put(new Person(), "String2");
treeMap.put(new Person(), "String3");
treeMap.put(new Person(), "String4");
System.out.println(treeMap);
After printing directly using System.out.println(treeMap); Iam only getting the last inserted value ie
Output:{Person#4aa36c=String4}
I know keys should be different but new operator always create a new object so I think its fine. But Iam helpless to figure out what is wrong going on here.

You are likely placing items in the Map backwards and would more than likely want the Person object as the value and the String as the key. But first you need to enhance your Person object with a Constructor to allow you to set the name at least, and maybe the age. Add a constructor to `Person':
public Person(String n, int a){
this.name = n;
this.age = a;
}
Then you can reorder how you add elements to the Map:
Person p1 = new Person("Bob Jones", 32);
treeMap.put(p1.getName(), person);
Additionally, TreeMap uses the compareTo method to determine where to place the entry in the Map for the keys. So, if you intend to use the Person object as the key, you need to implement the compareTo method. But your compareTo method is not implemented correctly and simply returns 0 for every element, causing your elements to overwrite each others location in the Map:
#Override
public int compareTo(Object o) {
/ TODO Auto-generated method stub
return 0;
}
You need to properly implement the method contents (Hence the TODO statement).
Maybe add a social security number (SSN) to the Person class:
Long ssn;
public void setSsn(Long value){
ssn = value;
}
public Long getSsn(){
return ssn;
}
Then you could compare on ssn:
#Override
public int compareTo(Object o) {
if(o == null) return 1;
Person op = (Person)o;
return ssn.compareTo(op.getSsn());
}
But if you just want to create some type of combination with what you have, maybe concatenate the name and age to try to have uniqueness:
#Override
public int compareTo(Object o) {
if(o == null) return 1;
Person op = (Person)o;
return (name + age).compareTo(op.getName() + op.getAge());
}

TreeMap uses compareTo method of Comparable (not equals method from Object) when it tries to put an element into Map. Since your compareTo method implementation returns 0 for all Person objects, there can only be one element in TreeMap.
So when you try to put multiple Person keys, TreeMap only updates value, since for it all Person objects are same (as per your implementation for compareTo).
Here is code snippet for TreeMap.put from Java-8
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
#SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
So first it checks, whether Comparator is provided, if yes, TreeMap uses compare method to compare keys otherwise it uses compareTo method of Comparable for comparison.

You overrode toCompare method incorrectly. It always returns 0. So, all objects in the tree will be interpreted as the same and your treeMap always contains only the value that had been added the last.
I suggest you consider the compact (but not general as #4castle noted) solution.
#Override
public int compareTo(Person o) {
if (age != o.age) return age > o.age ? 1 : -1;
return name.compareTo(o.name);
}
If you changed the first line of the class declaration to
public class Person implements Comparable<Pesron>

Keys aren't unique based on their memory address, they are unique based on having a unique value. All of your Person objects are practically identical, but what's more, all of your Person objects claim to be "equal" to any other Person object given your current compareTo implementation (0 means equal).
The parameter order for the put method is key first, value second. You need to rearrange the put statements so that something unique is in front (the string), and therefore make all the entries unique.
treeMap.put("String1", new Person());
treeMap.put("String2", new Person());
treeMap.put("String3", new Person());
treeMap.put("String4", new Person());
Now there's no need to implement Comparable or have the compareTo method in Person.
If you require that the Person object be a key, you will have to make each one unique by giving it a unique variable with which to use the compareTo method properly. The best solution is to add a required ID number to each Person object, or otherwise use the name and age as the unique value for the object. Add on this code to Person:
private int id;
private static int numPeople = 0;
public Person() {
numPeople++;
id = numPeople;
name = "";
}
#Override
public int compareTo(Person o) {
if (age != o.age) return age > o.age ? 1 : -1;
if (!name.equals(o.name) return name.compareTo(o.name);
return id > o.id ? 1 : -1;
}
with
public class Person implements Comparable<Person>

Related

Can we use equals() method to compare all content in an object? [duplicate]

I am trying to override equals method in Java. I have a class People which basically has 2 data fields name and age. Now I want to override equals method so that I can check between 2 People objects.
My code is as follows
public boolean equals(People other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(other.name) && age.equals(other.age);
} // end else
return result;
} // end equals
But when I write age.equals(other.age) it gives me error as equals method can only compare String and age is Integer.
Solution
I used == operator as suggested and my problem is solved.
//Written by K#stackoverflow
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K#stackoverflow
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
#Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output:
run:
-- Subash Adhikari - VS - K false
-- Subash Adhikari - VS - StackOverflow false
-- Subash Adhikari - VS - Subash Adhikari true
-- K - VS - StackOverflow false
-- K - VS - Subash Adhikari false
-- StackOverflow - VS - Subash Adhikari false
-- BUILD SUCCESSFUL (total time: 0 seconds)
Introducing a new method signature that changes the parameter types is called overloading:
public boolean equals(People other){
Here People is different than Object.
When a method signature remains the identical to that of its superclass, it is called overriding and the #Override annotation helps distinguish the two at compile-time:
#Override
public boolean equals(Object other){
Without seeing the actual declaration of age, it is difficult to say why the error appears.
I'm not sure of the details as you haven't posted the whole code, but:
remember to override hashCode() as well
the equals method should have Object, not People as its argument type. At the moment you are overloading, not overriding, the equals method, which probably isn't what you want, especially given that you check its type later.
you can use instanceof to check it is a People object e.g. if (!(other instanceof People)) { result = false;}
equals is used for all objects, but not primitives. I think you mean age is an int (primitive), in which case just use ==. Note that an Integer (with a capital 'I') is an Object which should be compared with equals.
See What issues should be considered when overriding equals and hashCode in Java? for more details.
Item 10: Obey the general contract when overriding equals
According to Effective Java, Overriding the equals method seems simple, but there are many ways to get it wrong, and consequences can be dire. The easiest way to avoid problems is not to override the equals method, in which case each instance of the class is equal only to itself. This is the right thing to do if any of the following conditions apply:
Each instance of the class is inherently unique. This is true for classes such as Thread that represent active entities rather than values. The equals implementation provided by Object has exactly the right behavior for these classes.
There is no need for the class to provide a “logical equality” test. For example, java.util.regex.Pattern could have overridden equals to check whether two Pattern instances represented exactly the same regular expression, but the designers didn’t think that clients would need or want this functionality. Under these circumstances, the equals implementation inherited from Object is ideal.
A superclass has already overridden equals, and the superclass behavior is appropriate for this class. For example, most Set implementations inherit their equals implementation from AbstractSet, List implementations from AbstractList, and Map implementations from AbstractMap.
The class is private or package-private, and you are certain that its equals method will never be invoked. If you are extremely risk-averse, you can override the equals method to ensure that it isn’t invoked accidentally:
The equals method implements an equivalence relation. It has these properties:
Reflexive: For any non-null reference value x, x.equals(x) must return true.
Symmetric: For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
Transitive: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
Consistent: For any non-null reference values x and y, multiple invocations of x.equals(y) must consistently return true or consistently return false, provided no information used in equals comparisons is modified.
For any non-null reference value x, x.equals(null) must return false.
Here’s a recipe for a high-quality equals method:
Use the == operator to check if the argument is a reference to this object. If so, return true. This is just a performance optimization but one that is worth doing if the comparison is potentially expensive.
Use the instanceof operator to check if the argument has the correct type. If not, return false. Typically, the correct type is the class in which the method occurs. Occasionally, it is some interface implemented by this class. Use an interface if the class implements an interface that refines the equals contract to permit comparisons across classes that implement the interface. Collection interfaces such as Set, List, Map, and Map.Entry have this property.
Cast the argument to the correct type. Because this cast was preceded by an instanceof test, it is guaranteed to succeed.
For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return true; otherwise, return false. If the type in Step 2 is an interface, you must access the argument’s fields via interface methods; if the type is a class, you may be able to access the fields directly, depending on their accessibility.
For primitive fields whose type is not float or double, use the == operator for comparisons; for object reference fields, call the equals method recursively; for float fields, use the static Float.compare(float, float) method; and for double fields, use Double.compare(double, double). The special treatment of float and double fields is made necessary by the existence of Float.NaN, -0.0f and the analogous double values; While you could compare float and double fields with the static methods Float.equals and Double.equals, this would entail autoboxing on every comparison, which would have poor performance. For array fields, apply these guidelines to each element. If every element in an array field is significant, use one of the Arrays.equals methods.
Some object reference fields may legitimately contain null. To avoid the possibility of a NullPointerException, check such fields for equality using the static method Objects.equals(Object, Object).
// Class with a typical equals method
public final class PhoneNumber {
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "area code");
this.prefix = rangeCheck(prefix, 999, "prefix");
this.lineNum = rangeCheck(lineNum, 9999, "line num");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
#Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNum == lineNum && pn.prefix == prefix
&& pn.areaCode == areaCode;
}
... // Remainder omitted
}
#Override
public boolean equals(Object that){
if(this == that) return true;//if both of them points the same address in memory
if(!(that instanceof People)) return false; // if "that" is not a People or a childclass
People thatPeople = (People)that; // than we can cast it to People safely
return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}
When comparing objects in Java, you make a semantic check, comparing the type and identifying state of the objects to:
itself (same instance)
itself (clone, or reconstructed copy)
other objects of different types
other objects of the same type
null
Rules:
Symmetry: a.equals(b) == b.equals(a)
equals() always yields true or false, but never a NullpointerException, ClassCastException or any other throwable
Comparison:
Type check: both instances need to be of the same type, meaning you have to compare the actual classes for equality. This is often not correctly implemented, when developers use instanceof for type comparison (which only works as long as there are no subclasses, and violates the symmetry rule when A extends B -> a instanceof b != b instanceof a).
Semantic check of identifying state: Make sure you understand by which state the instances are identified. Persons may be identified by their social security number, but not by hair color (can be dyed), name (can be changed) or age (changes all the time). Only with value objects should you compare the full state (all non-transient fields), otherwise check only what identifies the instance.
For your Person class:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Reusable, generic utility class:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* #param instance object instance (where the equals() is implemented)
* #param other other instance to compare to
* #param stateAccessors stateAccessors for state to compare, optional
* #param <T> instance type
* #return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
For your Person class, using this utility class:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}
Since I'm guessing age is of type int:
public boolean equals(Object other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(otherPeople.name) && age == otherPeople.age;
} // end else
return result;
} // end equals
if age is int you should use == if it is Integer object then you can use equals().
You also need to implement hashcode method if you override equals. Details of the contract is available in the javadoc of Object and also at various pages in web.
tl;dr
record Person ( String name , int age ) {}
if(
new Person( "Carol" , 27 ) // Compiler auto-generates implicitly the constructor.
.equals( // Compiler auto-generates implicitly the `equals` method.
new Person( "Carol" , 42 )
)
) // Returns `false`, as the name matches but the age differs.
{ … }
Details
While your specific problem is solved (using == for equality test between int primitive values), there is an alternative that eliminates the need to write that code.
record
Java 16 brings the record feature.
A record is a brief way to write a class whose main purpose is to transparently and immutably carry data. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
equals method provided automatically
The default implicit equals method compares each and every member field that you declared for the record. The members can be objects or primitives, both types are automatically compared in the default equals method.
For example, if you have a Person record carrying two fields, name & age, both of those fields are automatically compared to determine equality between a pair of Person objects.
public record Person ( String name , int age ) {}
Try it.
Person alice = new Person( "Alice" , 23 ) ;
Person alice2 = new Person( "Alice" , 23 ) ;
Person bob = new Person( "Bob" , 19 ) ;
boolean samePerson1 = alice.equals( alice2 ) ; // true.
boolean samePerson2 = alice.equals( bob ) ; // false.
You can override the equals method on a record, if you want a behavior other than the default. But if you do override equals, be sure to override hashCode for consistent logic, as you would for a conventional Java class. And, think twice: Whenever adding methods to a record, reconsider if a record structure is really appropriate to that problem domain.
Tip: A record can be defined within another class, and even locally within a method.
Here is the solution that I recently used:
public class Test {
public String a;
public long b;
public Date c;
public String d;
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Test)) {
return false;
}
Test testOther = (Test) obj;
return (a != null ? a.equals(testOther.a) : testOther.a == null)
&& (b == testOther.b)
&& (c != null ? c.equals(testOther.c) : testOther.c == null)
&& (d != null ? d.equals(testOther.d) : testOther.d == null);
}
}
For lazy programmers: lombok library is very easy and time saving. please have a look at this link
instead of writing lines of codes and rules, you just need to apply this library in your IDE and then just #Data and it is Done.
import lombok.Data;
#Data // this is the magic word :D
public class pojo {
int price;
String currency;
String productName;
}
in fact in the above code, #Data is a shortcut for
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#Getter
#Setter
#EqualsAndHashCode
#ToString
//or instead of all above #Data
public class pojo {
int price;
String currency;
String productName;
}

Why does my program not recognize two objects as equal when they have the same values? [duplicate]

I am trying to override equals method in Java. I have a class People which basically has 2 data fields name and age. Now I want to override equals method so that I can check between 2 People objects.
My code is as follows
public boolean equals(People other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(other.name) && age.equals(other.age);
} // end else
return result;
} // end equals
But when I write age.equals(other.age) it gives me error as equals method can only compare String and age is Integer.
Solution
I used == operator as suggested and my problem is solved.
//Written by K#stackoverflow
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K#stackoverflow
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
#Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output:
run:
-- Subash Adhikari - VS - K false
-- Subash Adhikari - VS - StackOverflow false
-- Subash Adhikari - VS - Subash Adhikari true
-- K - VS - StackOverflow false
-- K - VS - Subash Adhikari false
-- StackOverflow - VS - Subash Adhikari false
-- BUILD SUCCESSFUL (total time: 0 seconds)
Introducing a new method signature that changes the parameter types is called overloading:
public boolean equals(People other){
Here People is different than Object.
When a method signature remains the identical to that of its superclass, it is called overriding and the #Override annotation helps distinguish the two at compile-time:
#Override
public boolean equals(Object other){
Without seeing the actual declaration of age, it is difficult to say why the error appears.
I'm not sure of the details as you haven't posted the whole code, but:
remember to override hashCode() as well
the equals method should have Object, not People as its argument type. At the moment you are overloading, not overriding, the equals method, which probably isn't what you want, especially given that you check its type later.
you can use instanceof to check it is a People object e.g. if (!(other instanceof People)) { result = false;}
equals is used for all objects, but not primitives. I think you mean age is an int (primitive), in which case just use ==. Note that an Integer (with a capital 'I') is an Object which should be compared with equals.
See What issues should be considered when overriding equals and hashCode in Java? for more details.
Item 10: Obey the general contract when overriding equals
According to Effective Java, Overriding the equals method seems simple, but there are many ways to get it wrong, and consequences can be dire. The easiest way to avoid problems is not to override the equals method, in which case each instance of the class is equal only to itself. This is the right thing to do if any of the following conditions apply:
Each instance of the class is inherently unique. This is true for classes such as Thread that represent active entities rather than values. The equals implementation provided by Object has exactly the right behavior for these classes.
There is no need for the class to provide a “logical equality” test. For example, java.util.regex.Pattern could have overridden equals to check whether two Pattern instances represented exactly the same regular expression, but the designers didn’t think that clients would need or want this functionality. Under these circumstances, the equals implementation inherited from Object is ideal.
A superclass has already overridden equals, and the superclass behavior is appropriate for this class. For example, most Set implementations inherit their equals implementation from AbstractSet, List implementations from AbstractList, and Map implementations from AbstractMap.
The class is private or package-private, and you are certain that its equals method will never be invoked. If you are extremely risk-averse, you can override the equals method to ensure that it isn’t invoked accidentally:
The equals method implements an equivalence relation. It has these properties:
Reflexive: For any non-null reference value x, x.equals(x) must return true.
Symmetric: For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
Transitive: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
Consistent: For any non-null reference values x and y, multiple invocations of x.equals(y) must consistently return true or consistently return false, provided no information used in equals comparisons is modified.
For any non-null reference value x, x.equals(null) must return false.
Here’s a recipe for a high-quality equals method:
Use the == operator to check if the argument is a reference to this object. If so, return true. This is just a performance optimization but one that is worth doing if the comparison is potentially expensive.
Use the instanceof operator to check if the argument has the correct type. If not, return false. Typically, the correct type is the class in which the method occurs. Occasionally, it is some interface implemented by this class. Use an interface if the class implements an interface that refines the equals contract to permit comparisons across classes that implement the interface. Collection interfaces such as Set, List, Map, and Map.Entry have this property.
Cast the argument to the correct type. Because this cast was preceded by an instanceof test, it is guaranteed to succeed.
For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return true; otherwise, return false. If the type in Step 2 is an interface, you must access the argument’s fields via interface methods; if the type is a class, you may be able to access the fields directly, depending on their accessibility.
For primitive fields whose type is not float or double, use the == operator for comparisons; for object reference fields, call the equals method recursively; for float fields, use the static Float.compare(float, float) method; and for double fields, use Double.compare(double, double). The special treatment of float and double fields is made necessary by the existence of Float.NaN, -0.0f and the analogous double values; While you could compare float and double fields with the static methods Float.equals and Double.equals, this would entail autoboxing on every comparison, which would have poor performance. For array fields, apply these guidelines to each element. If every element in an array field is significant, use one of the Arrays.equals methods.
Some object reference fields may legitimately contain null. To avoid the possibility of a NullPointerException, check such fields for equality using the static method Objects.equals(Object, Object).
// Class with a typical equals method
public final class PhoneNumber {
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "area code");
this.prefix = rangeCheck(prefix, 999, "prefix");
this.lineNum = rangeCheck(lineNum, 9999, "line num");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
#Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNum == lineNum && pn.prefix == prefix
&& pn.areaCode == areaCode;
}
... // Remainder omitted
}
#Override
public boolean equals(Object that){
if(this == that) return true;//if both of them points the same address in memory
if(!(that instanceof People)) return false; // if "that" is not a People or a childclass
People thatPeople = (People)that; // than we can cast it to People safely
return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}
When comparing objects in Java, you make a semantic check, comparing the type and identifying state of the objects to:
itself (same instance)
itself (clone, or reconstructed copy)
other objects of different types
other objects of the same type
null
Rules:
Symmetry: a.equals(b) == b.equals(a)
equals() always yields true or false, but never a NullpointerException, ClassCastException or any other throwable
Comparison:
Type check: both instances need to be of the same type, meaning you have to compare the actual classes for equality. This is often not correctly implemented, when developers use instanceof for type comparison (which only works as long as there are no subclasses, and violates the symmetry rule when A extends B -> a instanceof b != b instanceof a).
Semantic check of identifying state: Make sure you understand by which state the instances are identified. Persons may be identified by their social security number, but not by hair color (can be dyed), name (can be changed) or age (changes all the time). Only with value objects should you compare the full state (all non-transient fields), otherwise check only what identifies the instance.
For your Person class:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Reusable, generic utility class:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* #param instance object instance (where the equals() is implemented)
* #param other other instance to compare to
* #param stateAccessors stateAccessors for state to compare, optional
* #param <T> instance type
* #return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
For your Person class, using this utility class:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}
Since I'm guessing age is of type int:
public boolean equals(Object other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(otherPeople.name) && age == otherPeople.age;
} // end else
return result;
} // end equals
if age is int you should use == if it is Integer object then you can use equals().
You also need to implement hashcode method if you override equals. Details of the contract is available in the javadoc of Object and also at various pages in web.
tl;dr
record Person ( String name , int age ) {}
if(
new Person( "Carol" , 27 ) // Compiler auto-generates implicitly the constructor.
.equals( // Compiler auto-generates implicitly the `equals` method.
new Person( "Carol" , 42 )
)
) // Returns `false`, as the name matches but the age differs.
{ … }
Details
While your specific problem is solved (using == for equality test between int primitive values), there is an alternative that eliminates the need to write that code.
record
Java 16 brings the record feature.
A record is a brief way to write a class whose main purpose is to transparently and immutably carry data. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
equals method provided automatically
The default implicit equals method compares each and every member field that you declared for the record. The members can be objects or primitives, both types are automatically compared in the default equals method.
For example, if you have a Person record carrying two fields, name & age, both of those fields are automatically compared to determine equality between a pair of Person objects.
public record Person ( String name , int age ) {}
Try it.
Person alice = new Person( "Alice" , 23 ) ;
Person alice2 = new Person( "Alice" , 23 ) ;
Person bob = new Person( "Bob" , 19 ) ;
boolean samePerson1 = alice.equals( alice2 ) ; // true.
boolean samePerson2 = alice.equals( bob ) ; // false.
You can override the equals method on a record, if you want a behavior other than the default. But if you do override equals, be sure to override hashCode for consistent logic, as you would for a conventional Java class. And, think twice: Whenever adding methods to a record, reconsider if a record structure is really appropriate to that problem domain.
Tip: A record can be defined within another class, and even locally within a method.
Here is the solution that I recently used:
public class Test {
public String a;
public long b;
public Date c;
public String d;
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Test)) {
return false;
}
Test testOther = (Test) obj;
return (a != null ? a.equals(testOther.a) : testOther.a == null)
&& (b == testOther.b)
&& (c != null ? c.equals(testOther.c) : testOther.c == null)
&& (d != null ? d.equals(testOther.d) : testOther.d == null);
}
}
For lazy programmers: lombok library is very easy and time saving. please have a look at this link
instead of writing lines of codes and rules, you just need to apply this library in your IDE and then just #Data and it is Done.
import lombok.Data;
#Data // this is the magic word :D
public class pojo {
int price;
String currency;
String productName;
}
in fact in the above code, #Data is a shortcut for
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#Getter
#Setter
#EqualsAndHashCode
#ToString
//or instead of all above #Data
public class pojo {
int price;
String currency;
String productName;
}

Why does my TreeSet not add anything beyond the first element?

I have several arrays in the form:
private static String[] patientNames = { "John Lennon", "Paul McCartney", "George Harrison", "Ringo Starr" };
Then I make a TreeSet like this:
TreeSet<Patient> patTreeSet = new TreeSet<Patient>();
Where Patient is a different class that makes "Patient" objects.
Then I loop through each element in my arrays to create several patients and add them to my patTreeSet like this:
for(int i = 0; i< patientNames.length; i++){
Date dob = date.getDate("MM/dd/yyyy", patientBirthDates[i]);
Patient p = new PatientImpl(patientNames[i], patientSSN[i], dob);
patTreeSet.add(p);
}
But when I go to check my patTreeSet.size() it only returns "1" - why is this?
I know my objects are working well because when I try to do the same thing but with ArrayList instead, everything works fine. So I'm guessing I'm using the TreeSet wrong.
If it helps, Patient has a method called getFirstName(), and when I try to do the following:
Iterator<Patient> patItr = patTreeSet.iterator();
while(patItr.hasNext()){
System.out.println(patItr.next().getFirstName());
}
Then only "John" prints, which obviously shouldn't be the case... So, am I totally misusing the TreeSet?
Thanks in advance for any help!
EDIT below
================PatientImpl Class====================
public class PatientImpl implements Patient, Comparable{
Calendar cal = new GregorianCalendar();
private String firstName;
private String lastName;
private String SSN;
private Date dob;
private int age;
private int thisID;
public static int ID = 0;
public PatientImpl(String fullName, String SSN, Date dob){
String[] name = fullName.split(" ");
firstName = name[0];
lastName = name[1];
this.SSN = SSN;
this.dob = dob;
thisID = ID += 1;
}
#Override
public boolean equals(Object p) {
//for some reason casting here and reassigning the value of p doesn't take care of the need to cast in the if statement...
p = (PatientImpl) p;
Boolean equal = false;
//make sure p is a patient before we even compare anything
if (p instanceof Patient) {
Patient temp = (Patient) p;
if (this.firstName.equalsIgnoreCase(temp.getFirstName())) {
if (this.lastName.equalsIgnoreCase(temp.getLastName())) {
if (this.SSN.equalsIgnoreCase(temp.getSSN())) {
if(this.dob.toString().equalsIgnoreCase(((PatientImpl) p).getDOB().toString())){
if(this.getID() == temp.getID()){
equal = true;
}
}
}
}
}
}
return equal;
}
and then all the getters are below, as well as the compareTo() method from the Comparable interface
If you put your objects in a TreeSet, you need to either provide an implementation of the Comparator interface in the constructor, or you need your objects to be of a class that implements Comparable.
You said you implement compareTo from the Comparable interface, but in your comment you say that you didn't, so am I correct in assuming that you just return 0; in the compareTo method? That would explain your problem, because TreeSet would then think that all your objects are 'the same' based on the compareTo method result.
Basically, in a TreeSet, your objects are maintained in a sorted order, and the sorting is determined by the outcome of the Comparable/Comparator method. This is used to quickly find duplicates in a TreeSet and has the added benefit that when you iterate over the TreeSet, you get the results in sorted order.
The Javadoc of TreeSet says:
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.
The easiest way to achieve that is to let your equals method call the compareTo method and check if the result is 0.
Given your PatientImpl class, I assume that you would want to sort patients first by their last name, then by their first name, and then by the rest of the fields in the class.
You could implement a compareTo method like this:
#Override
public int compareTo(Object o) {
if (!(o instanceof Patient))
return -1;
Patient temp = (Patient) o;
int r = this.lastName.compareToIgnoreCase(temp.getLastName());
if (r == 0)
r = this.firstName.compareToIgnoreCase(temp.getFirstName());
if (r == 0)
r = this.SSN.compareToIgnoreCase(temp.getSSN());
if (r == 0)
r = this.dob.toString().compareToIgnoreCase(temp.getDOB().toString());
if (r == 0)
r = Integer.compare(this.getID(), temp.getID());
return r;
}
I believe that would solve the problem you described.
I would advise you to read up (Javadoc or books) on TreeSet and HashSet and the importance of the equals, compareTo and hashCode methods.
If you want to put your objects in a Set or a Map, you need to know about these to implement that correctly.
Note
I based this compareTo method on your equals method.
You were comparing the date-of-birth by first calling toString. That's not a very good way of doing that - you can use the equals method in java.util.Date directly. In a compareTo method the problem gets worse because dates do not sort correctly when you sort them alphabetically.
java.util.Date also implements Comparable so you can replace that comparison in the method with:
if (r == 0)
r = this.dob.compareTo(temp.getDOB());
In addition, if any of the fields could be null, you need to check for that as well.

How to make HashMap work with Arrays as key?

I am using boolean arrays as keys for a HashMap. But the problem is HashMap fails to get the keys when a different array is passed as key, although the elements are same. (As they are different objects).
How can I make it work with arrays as keys ?
Here is the code :
public class main {
public static HashMap<boolean[], Integer> h;
public static void main(String[] args){
boolean[] a = {false, false};
h = new HashMap<boolean[], Integer>();
h.put(a, 1);
if(h.containsKey(a)) System.out.println("Found a");
boolean[] t = {false, false};
if(h.containsKey(t)) System.out.println("Found t");
else System.out.println("Couldn't find t");
}
}
Both the arrays a and t contain the same elements, but HashMap doesn't return anything for t.
How do I make it work ?
You cannot do it this way. Both t and a will have different hashCode() values because the the java.lang.Array.hashCode() method is inherited from Object, which uses the reference to compute the hash-code (default implementation). Hence the hash code for arrays is reference-dependent, which means that you will get a different hash-code value for t and a. Furthermore, equals will not work for the two arrays because that is also based on the reference.
The only way you can do this is to create a custom class that keeps the boolean array as an internal member. Then you need to override equals and hashCode in such a way that ensures that instances that contain arrays with identical values are equal and also have the same hash-code.
An easier option might be to use List<Boolean> as the key. Per the documentation the hashCode() implementation for List is defined as:
int hashCode = 1;
Iterator<E> i = list.iterator();
while (i.hasNext()) {
E obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
As you can see, it depends on the values inside your list and not the reference, and so this should work for you.
It is not possible to do this with arrays, as any two different arrays don't compare equals, even if they have the same elements.
You need to map from container class, for example ArrayList<Boolean> (or simply List<Boolean>. Perhaps BitSet would be even more appropriate.
Map implementations relies on key's equals and hashCode methods. Arrays in java are directly extends from Object, they use default equals and hashCode of Object which only compares identity.
If I were you, I would create a class Key
class Key {
private final boolean flag1;
private final boolean flag2;
public Key(boolean flag1, boolean flag2) {
this.flag1 = flag1;
this.flag2 = flag2;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof Key)) {
return false;
}
Key otherKey = (Key) object;
return this.flag1 == otherKey.flag1 && this.flag2 == otherKey.flag2;
}
#Override
public int hashCode() {
int result = 17; // any prime number
result = 31 * result + Boolean.valueOf(this.flag1).hashCode();
result = 31 * result + Boolean.valueOf(this.flag2).hashCode();
return result;
}
}
After that, you can use your key with Map:
Map<Key, Integer> map = new HashMap<>();
Key firstKey = new Key(false, false);
map.put(firstKey, 1);
Key secondKey = new Key(false, false) // same key, different instance
int result = map.get(secondKey); // --> result will be 1
Reference:
Java hash code from one field
Problems
As others have said, Java arrays inherit .hashcode() and .equals() from Object, which uses a hash of the address of the array or object, completely ignoring its contents. The only way to fix this is to wrap the array in an object that implements these methods based on the contents of the array. This is one reason why Joshua Bloch wrote Item 25: "Prefer lists to arrays." Java provides several classes that do this or you can write your own using Arrays.hashCode() and Arrays.equals() which contain correct and efficient implementations of those methods. Too bad they aren't the default implementations!
Whenever practical, use a deeply unmodifiable (or immutable) class for the keys to any hash-based collection. If you modify an array (or other mutable object) after storing it as a key in a hashtable, it will almost certainly fail future .get() or .contains() tests in that hashtable. See also Are mutable hashmap keys a dangerous practice?
Specific Solution
// Also works with primitive: (boolean... items)
public static List<Boolean> bList(Boolean... items) {
List<Boolean> mutableList = new ArrayList<>();
for (Boolean item : items) {
mutableList.add(item);
}
return Collections.unmodifiableList(mutableList);
}
ArrayList implements .equals() and .hashCode() (correctly and efficiently) based on its contents, so that every bList(false, false) has the same hashcode as, and will be equal to every other bList(false, false).
Wrapping it in Collections.unmodifiableList() prevents modification.
Modifying your example to use bList() requires changing just a few declarations and type signatures. It is as clear as, and almost as brief as your original:
public class main {
public static HashMap<List<Boolean>, Integer> h;
public static void main(String[] args){
List<Boolean> a = bList(false, false);
h = new HashMap<>();
h.put(a, 1);
if(h.containsKey(a)) System.out.println("Found a");
List<Boolean> t = bList(false, false);
if(h.containsKey(t)) System.out.println("Found t");
else System.out.println("Couldn't find t");
}
}
Generic Solution
public <T> List<T> bList(T... items) {
List<T> mutableList = new ArrayList<>();
for (T item : items) {
mutableList.add(item);
}
return Collections.unmodifiableList(mutableList);
}
The rest of the above solution is unchanged, but this will leverage Java's built-in type inference to work with any primitive or Object (though I recommend using only with immutable classes).
Library Solution
Instead of bList(), use Google Guava's ImmutableList.of(), or my own Paguro's vec(), or other libraries that provide pre-tested methods like these (plus immutable/unmodifiable collections and more).
Inferior Solution
This was my original answer in 2017. I'm leaving it here because someone found it interesting, but I think it's second-rate because Java already contains ArrayList and Collections.unmodifiableList() which work around the problem. Writing your own collection wrapper with .equals() and .hashCode() methods is more work, more error-prone, harder to verify, and therefore harder to read than using what's built-in.
This should work for arrays of any type:
class ArrayHolder<T> {
private final T[] array;
#SafeVarargs
ArrayHolder(T... ts) { array = ts; }
#Override public int hashCode() { return Arrays.hashCode(array); }
#Override public boolean equals(Object other) {
if (array == other) { return true; }
if (! (other instanceof ArrayHolder) ) {
return false;
}
//noinspection unchecked
return Arrays.equals(array, ((ArrayHolder) other).array);
}
}
Here is your specific example converted to use ArrayHolder:
// boolean[] a = {false, false};
ArrayHolder<Boolean> a = new ArrayHolder<>(false, false);
// h = new HashMap<boolean[], Integer>();
Map<ArrayHolder<Boolean>, Integer> h = new HashMap<>();
h.put(a, 1);
// if(h.containsKey(a)) System.out.println("Found a");
assertTrue(h.containsKey(a));
// boolean[] t = {false, false};
ArrayHolder<Boolean> t = new ArrayHolder<>(false, false);
// if(h.containsKey(t)) System.out.println("Found t");
assertTrue(h.containsKey(t));
assertFalse(h.containsKey(new ArrayHolder<>(true, false)));
I used Java 8, but I think Java 7 has everything you need for this. I tested hashCode and equals using TestUtils.
You could create a class that contains the array. Implements the hashCode() and equals() methods for that class, based on values:
public class boolarray {
boolean array[];
public boolarray( boolean b[] ) {
array = b;
}
public int hashCode() {
int hash = 0;
for (int i = 0; i < array.length; i++)
if (array[i])
hash += Math.pow(2, i);
return hash;
}
public boolean equals( Object b ) {
if (!(b instanceof boolarray))
return false;
if ( array.length != ((boolarray)b).array.length )
return false;
for (int i = 0; i < array.length; i++ )
if (array[i] != ((boolarray)b).array[i])
return false;
return true;
}
}
You can then use:
boolarray a = new boolarray( new boolean[]{ true, true } );
boolarray b = new boolarray( new boolean[]{ true, true } );
HashMap<boolarray, Integer> map = new HashMap<boolarray, Integer>();
map.put(a, 2);
int c = map.get(b);
System.out.println(c);
Probably it is because equals() method for Array returns acts different then you expect. You should think about implementing your own collecting and override equals() and hashCode().
boolean[] t;
t = a;
If you give this, instead of boolean[] t = {false, false};, then you'll get the desired output.
This is because the Map stores the reference as the key, and in your case, though t has the same values, it doesn't have the same reference as a.
Hence, when you give t=a, it'll work.
Its very similar to this:-
String a = "ab";
String b = new String("ab");
System.out.println(a==b); // This will give false.
Both a & b hold the same value, but have different references. Hence, when you try to compare the reference using ==, it gives false.
But if you give, a = b; and then try to compare the reference, you'll get true.
Map uses equals() to test if your keys are the same.
The default implementation of that method in Object tests ==, i.e. reference equality. So, as your two arrays are not the same array, equals always returns false.
You need to make the map call Arrays.equals on the two arrays to check for equality.
You can create an array wrapper class that uses Arrays.equals and then this will work as expected:
public static final class ArrayHolder<T> {
private final T[] t;
public ArrayHolder(T[] t) {
this.t = t;
}
#Override
public int hashCode() {
int hash = 7;
hash = 23 * hash + Arrays.hashCode(this.t);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ArrayHolder<T> other = (ArrayHolder<T>) obj;
if (!Arrays.equals(this.t, other.t)) {
return false;
}
return true;
}
}
public static void main(String[] args) {
final Map<ArrayHolder<Boolean>, Integer> myMap = new HashMap<>();
myMap.put(new ArrayHolder<>(new Boolean[]{true, true}), 7);
System.out.println(myMap.get(new ArrayHolder<>(new Boolean[]{true, true})));
}
You could use a library that accepts an external hashing and comparing strategy (trove).
class MyHashingStrategy implements HashingStrategy<boolean[]> {
#Override
public int computeHashCode(boolean[] pTableau) {
return Arrays.hashCode(pTableau);
}
#Override
public boolean equals(boolean[] o1, boolean[] o2) {
return Arrays.equals(o1, o2);
}
}
Map<boolean[], T> map = new TCustomHashMap<boolean[],T>(new MyHashingStrategy());

How to initialize Key Class in HashMap in Java

I have a question related to Key Type initialization in HashMap. For example, I have defined the below Node class with over-ridden equals() and hashCode() as follows:
public class Node implements Comparable<Node> {
private int Id;
public Node(int i) {
...
}
void setId(int i) {
Id = i;
}
int getId() {
return Id;
}
#Override
public boolean equals(Object o) {
if (o == null) {
throw new NullPointerException();
}
if (o instanceof Node && this != o) {
if (((Node) o).getId() == this.getId())
return true;
}
return false;
}
public int hashCode() {
return Id;
}
}
Now I am building a HashMap with key as type Node as follows:
public class AdjList {
public HashMap<Node,Double> adj;
public AdjList() {
adj = new HashMap<Node,Double>(maxSize);
}
...
}
As you can possibly figure out, I am trying to generate a graph adjacency list with the node class as HashMap.
Now, my question is when I call AdjList() constructor where I create a new HashMap with some maxSize, will it initialize the Node() class as key type? Or I need to separately initialize Node() clas for the key? If I need to initialize Node() in AdjList constructor, then how it can be possible?
Any suggestion will be valuable and useful suggestions will be rewarded.
Thanks,
Somnath
when I call AdjList() constructor where I create a new HashMap with some maxSize, will it initialize the Node() class as key type?
No! You are instantiate the Map with initialCapacity which is the loadFactor value not maxSize (See the documentation).
You may define a method in AddList that adds an entry.
public void add(int i,Double d)
{
adj.put(new Node(i),d);
}
Second, you've implemented Comparable so you must have to define the compareTo method.
There are a number of things that confuse me about your question (even after reading about adjacency lists which I had not heard of before). Firstly why is overriding equals and hash map important? Secondly how does this code form an adjacency list (reading http://en.wikipedia.org/wiki/Adjacency_list) I could not see the relationship between what you have and an adjacency list. I'm also not sure what you are asking when talking about initialising the Node class. It sounds like yoga re confused about some things, I'm just not sure what.
Finally, based on what I read about adjacency lists, I would simply use the following code:
Map<int, List<int>> adjList = new HashMap<int, List<int>>();
Then I can store node 1 with a adjacency list of nodes 2 and 3 as:
adjList.put(1, Arrays.aslist(2,3));
and retrieve then with:
adjList.get(1);
etc, etc. No need for any custom classes at all.

Categories