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.
Related
I have these two java classes as below:
RelocationDisabledProduct
public class RelocationDisabledProduct {
#JsonProperty("productIdentifierType")
private ProductIdentifierType productIdentifierType;
#JsonProperty("id")
private String id;
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
final Item that = (Item)o;
return Objects.equals(id, that.getId()) &&
Objects.equals(productIdentifierType, that.getProductIdentifierType());
}
#Override
public int hashCode() {
return Objects.hash(id, productIdentifierType);
}
}
Item
public class Item {
private String id;
private ProductIdentifierType productIdentifierType;
}
equals works fine
relocationDisabledProduct.equals(item) // true
but contains does not
List<RelocationDisabledProduct> relocationDisabledProducts;
...
getRelocationDisabledProducts().contains(item) // false
Since contains uses the equals method, not sure why my above syntax returns false?
I believe this is due to the definition of AbstractCollection<E> contains(Object o). The Javadoc says
public boolean contains​(Object o)
Returns true if this collection contains the specified element. More formally, returns true if and only if this collection contains at least one element e such that Objects.equals(o, e).
Note the order of arguments to contains(). It is using the equals() method from Item, not the one from RelocationDisabledProduct, which you would have probably seen if you stepped into the code in the debugger.
Both Item and RelocationDisabledProduct need to share a common equals() implementation, maybe a default method of a superinterface... you'll have to work out what makes sense in your case.
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!
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;
}
}
Is there a an implementation of the HashMap class (or Map interface) that will allow me to use alternate hashcode and equals operations...
Similar to how collections of the same type can be sorted in multiple ways using the Comparator in Collections.sort(list, comparator).
I would like to avoid if possible, creating a key wrapper providing the desired hashcode and equals operations.
In my case, one of the scenarios why I need something like this:
In my web application, for each request, I load the Location/ISP and other data.
In different parts of code (in my service and repository layers) I have "minimized" caches specific to its requirement.
Here is a simplified code example:
class GeoIpData{
private String countryName;
private String state;
private String city;
private String isp;
#Override
public int hashCode() {
//countryName hashCode
//state hashCode
//city hashCode
//isp hashCode
}
#Override
public boolean equals(Object obj) {
// compare countryName
// compare state
// compare city
// compare isp
}
}
Map<GeoIpData,#Type1> fullCache = ... //This cache needs to be unique per countryName,state,city and isp
Map<GeoIpData,#Type2> countryCache = ... //This cache needs to be unique per countryName
Map<GeoIpData,#Type2> ispCache = ... //This cache needs to be unique per countryName,isp
To achieve this the above 3 maps need 3 different hashcode and equals methods.
fullCache:
hashCode -> GeoIpData.hashCode();
equals -> GeoIpData.equals(Object obj);
countryCache:
hashCode -> {countryName hashCode }
equals -> {compare countryName }
ispCache:
hashCode -> {countryName hashCode & isp hashCode }
equals -> {compare countryName & compare isp hashCode }
GNU Trove allows you to provide specific TObjectHashingStrategy with your own hash and equals functions for TCustomHashMap.
Initial Java API bounded equals / hashCode behavior to type (class) because they didn't have lambdas back those days.
In order to reuse existing API / implementations - make ephemeral keys:
public CountryKey {
private String country;
#Override
public boolean equals(Object obj) {
if (!(obj instanceof CountryKey)) { return false; }
CountryKey that = (CountryKey) obj;
return this.country.equals(that.country);
}
#Override int hashCode() {
return country.hashCode();
}
}
or wrappers:
public CountryKey {
private GeoIpData holder;
#Override
public boolean equals(Object obj) {
if (!(obj instanceof CountryKey)) { return false; }
CountryKey that = (CountryKey) obj;
return this.holder.getCountry().equals(that.holder.getCountry());
}
#Override int hashCode() {
return holder.getCountry().hashCode();
}
}
and write helper constructors for keys:
public class GeoIpData {
public CountryKey buildCountryKey() {
return new CountryKey(this.country);
}
}
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.