2

How can I avoid inserting duplicate elements in a Set? If I have:

 Set<User> user=new HashSet<>();
                            User user1=new User("11","Mark",null,"1");
                            User user2=new User("11","Mark",null,"1");
                            User user3=new User("12","Helen",null,"2");

                            user.add(user1);
                            user.add(user2);
                            Log.d("main_activity_user", "la dimensione è" +String.valueOf(user.size()));

Adn User class is:

public class User {
    public String uid;
public String name;
    public String pversion;
public String upicture;
    public User(String uid,
            String name,
                String upicture, String pversion ){
        this.uid=uid;
        this.name=name;
        this.upicture=upicture;
        this.pversion=pversion;
    }
    public String get_uid(){
        return uid;
    }
    public String get_name(){
        return name;
    }
    public String get_pversion(){
        return pversion;
    }
    public String get_upicture(){
        return upicture;
    }
    @Override
    public boolean equals(Object obj) {
        User newObj = (User)obj;
        if (this.get_uid().equals( newObj.get_uid()))
            return true;
        else
            return false;
    }
}

Now the Set also stores duplicates and prints me 3 elements instead of two. Why?

I have never used the Set class before and I don't understand it. So, every time I use the Set class, do I have to Override the Equals method? Why? Doesn't the class delete duplicates automatically?

Dan
  • 3,647
  • 5
  • 20
  • 26
Elly
  • 345
  • 1
  • 8
  • 4
    Did you override `hashCode()`? – Eran Jun 14 '22 at 08:11
  • so for this we use the equals method? because every time I add an element to Set this is compared with the others with the Equals method? right? But I have used the Equals () method in the User class and it does not eliminate the duplicates , why? – Elly Jun 14 '22 at 08:14
  • 4
    You have to override `hashCode()` in conjunction with `equals(Object)`. Besides that, you should test with `instanceof` before casting to `User`. Further, don’t write `if(condition) return true; else return false;` Just write `return condition;`. Further, don’t write `"string" + String.valueOf(expression)`. You can simply write `"string" + expression`. – Holger Jun 14 '22 at 08:14
  • 2
    In java there is contract between `equals` and `hashCode`. This tutorial explains well, why your code works, as it is working now: https://www.baeldung.com/java-equals-hashcode-contracts – Bartek Jablonski Jun 14 '22 at 08:17
  • 2
    Many things slightly wrong with your code. You should always override hashCode() and equals() together, especially when thinking about HASH sets. Then follow java naming conventions. Do NOT use the "_" in your variable or method names. – GhostCat Jun 14 '22 at 08:46

1 Answers1

3

As it has been already said in the comments, your User class needs to honor the hashcode and equals contracts by overriding the equals() and hashCode() methods.

https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()

In your code, you're using a HashSet which is implemented as a HashMap under the hood. Instead, a HashMap is implemented as an array of buckets, where each entry is stored within a bucket based on the key's hashCode(). However, different keys may yield same hashcodes, so multiple entries may be listed within a same bucket. At that point, the HashMap has to resort to the equals() method to find the exact key within a bucket which corresponds to the inputted entry in order to retrieve or replace an element. This brief explanation shows you why it is so crucial to provide a proper definition of the hashCode() and equals() methods, because, as you could see, a HashMap heavily relies on these methods.

https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html

Here is a proper implementation of your User class where two users are said identical if they have same: uid, name, pversion and upicture. Instead, if two users are identical only by some of the mentioned fields, then you need to updated your equals() and hashCode() methods accordingly (they both must be based on the same fields).

public class User {
    public String uid;
    public String name;
    public String pversion;
    public String upicture;

    public User(String uid, String name, String upicture, String pversion) {
        this.uid = uid;
        this.name = name;
        this.upicture = upicture;
        this.pversion = pversion;
    }

    public String getUid() {
        return uid;
    }

    public String getName() {
        return name;
    }

    public String getPversion() {
        return pversion;
    }

    public String getUpicture() {
        return upicture;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(uid, user.uid) && Objects.equals(name, user.name) && Objects.equals(pversion, user.pversion) && Objects.equals(upicture, user.upicture);
    }

    @Override
    public int hashCode() {
        return Objects.hash(uid, name, pversion, upicture);
    }
}

Test Main

public class Main {
    public static void main(String[] args) {
        Set<User> user = new HashSet<>();
        User user1 = new User("11", "Mark", null, "1");
        User user2 = new User("11", "Mark", null, "1");
        User user3 = new User("12", "Helen", null, "2");

        user.add(user1);
        user.add(user2);
        System.out.println("la dimensione è: " + user.size());
    }
}

Output

la dimensione è: 1
Dan
  • 3,647
  • 5
  • 20
  • 26