Is it proper equals method for my class Java - java

As in subject: Is it proper equals method for my class Java? I have generated it automaticly by Eclipse I don't know if it makes vector.remove(pracownik) will work correctrly. Or is it wrong to generate it by Eclipse?
import java.util.Date;
import java.util.Vector;
public class Pracownik extends Osoba {
private String stanowisko;
private int pensja;
private Date dataZatrudnienia;
public Pracownik(Adres adres, String telefon, String imie, String nazwisko,
int id, Date dataUrodzenia, String stanowisko, int pensja,
Date dataZatrudnienia) {
super(adres, telefon, imie, nazwisko, id, dataUrodzenia);
this.stanowisko = stanowisko;
this.pensja = pensja;
this.dataZatrudnienia = dataZatrudnienia;
}
public String getStanowisko() {
return stanowisko;
}
public int getPensja() {
return pensja;
}
public Date getDataZatrudnienia() {
return dataZatrudnienia;
}
#Override
public String toString() {
return super.toString() + "\nstanowisko=" + stanowisko + "\npensja="
+ pensja + "\ndataZatrudnienia=" + dataZatrudnienia;
}
private static Vector<Pracownik> ekstensja = new Vector<Pracownik>();//kolekcja zawierajaca ekstensje
private static void dodajPracownik(Pracownik pracownik) { //metoda dodajac aobiekt do ekstensji
ekstensja.add(pracownik);
}
private static void usunPracownik(Pracownik pracownik) {//metoda usuwajaca obiekt z ekstensji
ekstensja.remove(pracownik);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pracownik other = (Pracownik) obj;
if (dataZatrudnienia == null) {
if (other.dataZatrudnienia != null)
return false;
} else if (!dataZatrudnienia.equals(other.dataZatrudnienia))
return false;
if (pensja != other.pensja)
return false;
if (stanowisko == null) {
if (other.stanowisko != null)
return false;
} else if (!stanowisko.equals(other.stanowisko))
return false;
return true;
}
private static void pokazEkstensje(){ //wyswietlenie ekstensji przy pomocy petli for each
System.out.println("Ekstensja klasy Pracownik");
for(Pracownik pracownik: ekstensja)
System.out.println(pracownik);
System.out.println();
}
public static void main(String[] args){
Adres adres = new Adres("tara", "588 m.42", "03-422", "Warszawa");
Pracownik pracownik = new Pracownik(adres, "02-6451-4564", "Ala", "Kotowa", 323, new Date(), "szef", 14000, new Date()); //tworze pracownika
System.out.println(pracownik);//wyswietlam pracowanika
//tworze stazyste
Stazysta stazysta = new Stazysta(adres, "3232 9898", "frajer", "costam", 3232, new Date(), "podawanie kawy", 0, new Umowa(new Date(2010,10,5), new Date(2011,11,8)));
//wysswietlam stazyste
System.out.println(stazysta);
}
}

Generating the equals method using Eclipse is fine. It is important to make sure a field is included in the generation if, and only if, it affects logical equality of your objects.
When you override equals, you should also override hashCode.
In general, when overriding inherited methods you need to ensure the new methods conform to any rules stated in the superclass. The Object hashCode documentation states several rules, including "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."
The hashCode you inherit from Object follows that rule - if the equals method is also the one inherited from Object. It does not follow that rule when used with your equals method.
If you let Eclipse do its "Generate hashCode() and equals()" thing, it will get it right. If you write an equals method manually, you need to write your own hashCode to match.
As a practical matter, a class that does not follow the Object hashCode contract is a trap for future reuse. Hashed data structures, such as HashMap and HashSet, may fail to find an object that is actually present if it has a broken hashCode method. One lesson I've learned the hard way is that it is a mistake to depend on "I'll never use this that way.". It is much better to keep things safe as one goes along.

I am not expert on this but I believe double equals are comparing address rather than content of a variable. So, you may change it to .equals(). I hope someone with much more experience correct me if I am wrong.

Related

Writing an hashcode function

I want to write a Hashcode function for my class. The equals method is already existing. What could serve as a good hashcode() for below class having mentioned fields.
public class ReservationSHC extends AuditableModel {
/** The log instance for this class * */
private static Log log = Log.getLog("RESERVATION");
private Long id;
private String shc;
private boolean systemGenerated;
private boolean notifyableLoadOrDGR;
private Integer versionId;
private String ownerCarrierCode;
private String shcDesc;
private String shcCategory;
private CargoRecord cargoRecord;
private ShipmentReservation shipmentReservation;
private Set additionalDataElements;
private boolean autoGenerated;
Equals() method is already existing for the class.
public boolean equals(Object arg0) {
if (arg0 instanceof ReservationSHC) {
ReservationSHC reservationSHC = (ReservationSHC) arg0;
if (reservationSHC.getShc().equals(this.shc)) {
return true;
}
} else if(arg0 instanceof String) {
String tempShc = (String) arg0;
if (tempShc.equals(this.shc)) {
return true;
}
}
return super.equals(arg0);
}
Also, I don't see any #override annotation on the equals method. Is it getting overridden??
As a rule of thumb, just use your IDE to generate the equals() and hashCode() methods selecting the relevant fields.
In this case, it looks like only shc is relevant to equality of objects, so these will be the methods (as generated by intelliJ )
import java.util.Objects;
.
.
.
private String shc;
.
.
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hello hello = (Hello) o;
return shc.equals(hello.shc);
}
#Override
public int hashCode() {
return Objects.hash(shc);
}
In your case i think you should re-evaluate the equals method too. Equating the object to the string seems incorrect.
The purpose of the annotation #Override is only meant to give you compiler warnings in case you do something wrong.
Example: if you changed your signature to
#Override
public boolean equals(String other)
then the compiler would know tell you that "no, this equals() method doesn't override anything" (because it uses String for its argument, but it must be other.
And note: your equals() implementation is wrong. When any x.equals(y) is true, then y.equals(x) needs to be true, too. And your idea to directly compare against Strings violates that condition. Example:
yourObject.equals("some"); // could give true, when yourObject.shc is "some"
but
"some".equals(yourObject); // will never be true, because yourObject isn't a string
Finally, you are comparing only one field, so that field should go into both methods, as written in the other answer!

Compare new elements in set with old elements in another set - Java

I have Set of Object which has existing values and getting a new Set values as an update. If the new Set does contain the old Object then I do nothing, if the new Set contains new Object then I want send create update and if the new Set doesn't contain an existing object then I want send a delete update.
Object has two fields :
private PreferenceType preferenceType;
private String preferenceValue;
Currently I am comparing the existing Set objects against new Set objects and if I don't find any existing object in new set then sending the delete update.
private void sendDeletePreferenceMessage(Set<AccountPreference> existingAccountPreferences, Set<AccountPreference> accountPreferencesFromRequest) {
int counter = 0;
for(AccountPreference accountPreference : existingAccountPreferences) {
for(AccountPreference accountPreference1: accountPreferencesFromRequest) {
if(accountPreference.getPreferenceType().equals(accountPreference1.getPreferenceType()) &&
accountPreference.getPreferenceValue().equals(accountPreference1.getPreferenceValue()))
counter++;
}
if(counter == 0) {
accPrefDeleteSender.send(accountPreference);
}
}
}
And also comparing the new set of objects against existing set of Objects to find the new updates that I want send as a create update
private void sendCreatePreferenceMessage(Set<AccountPreference> accountPreferencesFromRequest, Set<AccountPreference> existingAccountPreferences) {
int counter = 0;
for(AccountPreference accountPreference : accountPreferencesFromRequest) {
for(AccountPreference accountPreference1: existingAccountPreferences) {
if(accountPreference.getPreferenceType().equals(accountPreference1.getPreferenceType()) &&
accountPreference.getPreferenceValue().equals(accountPreference1.getPreferenceValue()))
counter++;
}
if(counter == 0) {
accPrefCreateSender.send(accountPreference);
}
}
}
This works perfectly but I believe this could be simplified in a better way. Any suggestion of doing this better!
You are using two AccountPreference Objects: one with id field, and another without id. I don't know if it's possible without using interfaces or abstract classes. Nevertheless, according to your comments it seems to me you're confused how to override Object.equals method, so I'll give you an example:
class MyObjectWithId {
private long id;
private String someField;
public String getSomeField() {
return someField;
}
#Override
public boolean equals(Object other){
if (this == other) return true;
if (other == null || !other.getClass().isInstance(MyObject.class)) return false;
MyObject myObject = (MyObject) other;
return this.getSomeField().equals(myObject.getSomeField());
}
}
class MyObject {
private String someField;
public String getSomeField() {
return someField;
}
#Override
public boolean equals(Object other){
if (this == other) return true;
if (other == null || !other.getClass().isInstance(MyObjectWithId.class)) return false;
MyObjectWithId myObjectWithId = (MyObjectWithId) other;
return this.getSomeField().equals(myObjectWithId.getSomeField());
}
}
As you can see, you can ask if the two different types of Object are equal (in this case, there isn't need for overriding hashCode).
Now you should be able to do the rest (regarding your original question about two Sets)
Set has a method boolean contains(Object o).
You don't need two for loops, just iterate through one Set and check if another Set contains() the object!
You also need to override the boolean equals(Object o) in the POJO!
class MyObject {
private PreferenceType preferenceType;
private String preferenceValue;
#Override
public boolean equals(Object o){
if (this == o)
return true;
if (o == null || o.getClass()o.getClass() != getClass())
return false;
if(this.getPreferenceType().equals(o.getPreferenceType()) && this.getPreferenceValue().equals(o.getPreferenceValue()))
return true;
return false;
}
}
}

ArrayList removeAll() not removing objects

I have the simple ArrayLists of the member class:
ArrayList<Member> mGroupMembers = new ArrayList<>();
ArrayList<Member> mFriends = new ArrayList<>();
Member class:
public class Member {
private String userUID;
private String userName;
public String getUserUID() {
return userUID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setUserUID(String userUID) {
this.userUID = userUID;
}
}
The ArrayList for friends contains all the users friends. What I simply wish to do is remove from the friends list, group members if present with:
mFriends.removeAll(mGroupMembers);
Yet it does nothing to the mFriends list...
Looking at the log statements, the friend does in fact appear within the mGroupMember list.
Why doesn't this work?
How are 2 members determined to be equal? I'm guessing if they have the same ID, you deem them equal, however java wants them to be the exact same reference in memory which may not be the case. To correct for this you can override the equals function to have it return if the ids are equal:
public class Member {
//..
#Override
public boolean equals(Object anObject) {
if (!(anObject instanceof Member)) {
return false;
}
Member otherMember = (Member)anObject;
return otherMember.getUserUID().equals(getUserUID());
}
}
Also when you override .equals it is recommended to also override hashCode so that the objects also work correctly in hashing functions like Set or Map.
You have to understand the sequence of invocation underneath it
-> ArrayList#removeAll(Collection)
|
| calls
|
|--> ArrayList#contains(Object)
|
| calls
|
|--> ArrayList#indexOf(Object)
|
| calls
|
|--> Object#equals
So if equals is not correctly overridden (following the equals contract rules), you're not getting the correct behaviour.
As mentioned in the comments, elements from the ArrayList will only be removed if their equals() method returns true. The non-overridden method checks for equality based on reference (i.e. they must be the same object in memory).
What you probably want is to override equals to be based on the properties of Member, such as in the example below:
#Override
public void equals(Object o) {
if(o == null) {
return false;
} else if (!(o instanceof Member)) {
return false;
} else {
return ((Member) o).getUserUID().equals(this.userUID) && ((Member) o).getUserName().equals(this.userName);
}
}
In addition, you should override hashCode() when overriding equals() so that when two objects are equal, they have the same hash code. The non-overriden implementation of hashCode is also based on equality by reference.

Java HashMap containsKey strange behavior

Trying to realize simple task stuck into strange problem:
class User{
String login;
String pwrd;
User(String lg,String pw){
this.login=lg;
this.pwrd=pw;
}
public String toString(){
return this.login;
}
public boolean equals(String a){
return this.login.equals(a);
}
public boolean equals(User t){
return this.login.equals(t.toString());
}
}
public class Foo{
public static void main (String[] args)
{
HashMap<User,Boolean> a=new HashMap<>();
User a1=new User("asd","123"),a2=new User("asd","134");
a.put(a1,false);
a.put(a2,false);
System.out.println(a.containsKey(a2));
System.out.println(a.containsKey("asd"));
}
}
As a result I expected both containsKey checks to be true. Further in code it would be used more and more. So the first thing is to understand why it behaves so and if possible fix it. Any help appreciated.
The keys of your Map are User instances, so a.containsKey("asd") will never return true, since "asd" is a String.
BTW, you didn't override Object's equals, which expects an Object argument. This means that a.containsKey(a2) also returns false, since a1==a2 is false.
A correct implementation of equals would be :
#Override
public boolean equals(Object other){
if (!(other instanceof User))
return false;
User u = (User) other;
return this.login.equals(u.login);
}
As Andy mentioned, you must also override hashCode, so that if a.equals(b) is true then a.hashCode()==b.hashCode().
EDIT :
I think you can make a.containsKey("asd") return true if you override equals in a way that treats String instances as equal to your User instance if they match the login property :
#Override
public boolean equals(Object other){
if (other instanceof User) {
User u = (User) other;
return this.login.equals(u.login);
} else if (other instanceof String) {
String u = (String) other;
return this.login.equals(u);
}
return false;
}
#Override
public int hashCode()
{
return login.hashCode();
}
I never tried such an implementation of equals, but based on my understanding of HashMap, it might work.
However, such an implementation of equals would violate the contract of equals as defined in the Javadoc of Object, since "asd".equals(a1) will return false even though a1.equals("asd") is true.
EDIT:
After checking the implementation of HashMap, I found this implementation of equals won't work, since the code of containsKey(key) compares the key against the keys of the existing entries instead of the other way round, and String.equals(obj) will always return false if obj is not a String. I guess there's a good reason not to break the contract of equals.
You need to override public boolean equals(Object other) and inside that method check for correct type of other object passed. Please note that the object passed might be null as well.
public class User {
public boolean equals(Object other) {
//Is the same
if(other == this) {
return true;
}
//Other is a user as well - Includes null-check (thanks, Kevin!)
if(other instanceof User) {
//equal if usernames are equal
return login.equals(other.login);
}
//anything else - not equal / null, whatever
return false;
}
}

Java indexof() search

I have a java LinkedList which contains several custom objects of the same type.
LinkedList<myClass> = new LinkedList<myClass>();
Within my objects I have a specific value
class myClass(){
public int id;
}
I want to be able to return the index of the linked list for a match of a specific value, i.e: Find the LinkedList index where the object id = 7
I have looked into using indexof, contains, and containsall, but without any luck (index of always returns -1).
Is this something I can do with a prebuild libary, or am I going to have to extend my own search function for custom objects?
Override the equals method on your myClass class so the LinkedList could find the object:
public class myClass {
private int id; //it should be private, not public
//other attributes...
//getters and setters...
#Override
public void equals(Object o) {
if (o == null) return false;
if (o == this) return true;
if (o instanceof myClass) {
myClass x = (myClass)x;
return x.getId() == this.id;
}
return false;
}
}
Since you're overriding equals, you should also override the hashCode method:
#Override
public int hashCode() {
return this.id;
}
The reason for this is explained in the Object class javadoc:
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.
Maybe you shoud simple store your objects in an HashMap<key,value>
you put an object in it as value with a key. If you want to search for an Object you just get it over the key. So for example you take your class and use the objectID as key if its unique.
HashMap<Integer, myClass> list = new HashMap<Integer, myClass>();
list.put(newId, new MyClass(newId)); //just an example!
to find it now you just need one line like this:
list.get(newId);
if the newId does not exsist it return null.
LinkedList compares Object using their equals() method. So if you want two instances of your class to be considered equal when they have the same ID, you must override the equals() method:
#Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null) {
return false;
}
if (o.getClass() == this.getClass()) {
return this.id == ((MyClass) o).id;
}
return false;
}
When overriding equals(), hashCode() must also be overridden, because two equals objects MUST have the same hashCode:
#Override
public int hashCode() {
return id;
}
Note that if you don't want two instances to be considered equal when they have the same ID, then you have no choice other than iterating the list and finding the first element which has the same ID as the instance you're looking for. Or you must use another data structure, like a Map<Integer, MyClass> for example.
This can be implemented in terms of List's indexOf() method, all you have to do is override equals() and hashChode() in myClass to specify that the comparisons must be made using the id attribute (here is an explanation of why you need to override both methods). Simply add this methods to myClass:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
myClass other = (myClass) obj;
if (id != other.id)
return false;
return true;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
Now to find the index of an element with id == 7 do something like this:
int idx = myList.indexOf(new myClass(7));
That is, assuming that there exists a constructor in myClass that takes the id as a parameter.
You can do it like this
list.indexOf(new Object() {
#Override
public boolean equals(Object o) {
MyClass mine = (MyClass) o;
return mine.id == yourValue;
}
});

Categories

Resources