LinkedHashSet remove duplicates object

17,196

Solution 1

All Set implementations remove duplicates, and the LinkedHashSet is no exception.

The definition of duplicate is two objects that are equal to each other, according to their equals() method. If you haven't overridden equals on your Product class, then only identical references will be considered equal - not different instances with the same values.

So you need to add a more specific implementation of equals (and hashcode) for your class. For some examples and guidance, see Overriding equals and hashcode in Java. (Note that you must override hashcode as well, otherwise your class will not behave correctly in hash sets.)

Solution 2

I won't give you straight answer, but a couple of advices.

  1. If you want to put Product in a Set you need to implement its equals() and hashCode() methods.
  2. When implementing equals() you will have to decide what "equality" for a Product means, (Set can contain only one instance in terms on "equality"). For instance, if two Product instances are "equal" is it enough if they have the same ID or should we also take quantity into account?. It's not easy to answer this question in your case, but please read on.
  3. Normally in situation like this only ID is taken into account. In that case you shouldn't have two Product instances in memory with different quantities, because one of them would represent a state that is incorrect (ie. particular product's quantity can be either 1 or 3, not both at a time).
  4. I think the design is not fully correct. Product class in your case represents a general product description (including price), so quantity doesn't really fit there. If someone can order a couple of copies of Product I think you should create another class such as Order or OrderLine which specifies what product is ordered and the corresponding quantity, eg:

    class OrderLine {
      private Product product;
      private Integer quantity; 
    }
    

    With design like this it's easy to answer question from point 2. Product.equals() should compare the ID only, and OrderLine.equals() both the product (ID) and quantity.

Solution 3

I would suggest you implement your own hash function in a way that it hashes elements with equal ID-s with the same code. This will solve your issue, without you having to code it explicitly.

Share:
17,196
insict
Author by

insict

Updated on June 11, 2022

Comments

  • insict
    insict almost 2 years

    I have simple question to you, I have class Product that have fields like this:

    private Integer id;
    private String category;
    private String symbol;
    private String desc;
    private Double price;
    private Integer quantity;
    

    I want to remove duplicates item from LinkedHasSet based on ID, e.g Products that have same ID but diffrent quantity will be add to set, I want to remove (update) products with same ID, and it will by my unique id of object, how to do that?

    e.g Product: id=1, category=CCTV, symbol=TVC-DS, desc=Simple Camera, price=100.00, quantity=1 Product: id=1, category=CCTV, symbol=TVC-DS, desc=Simple Camera, price=100.00, quantity=3

    won't be added to set

    my code:

        public void setList(Set<Product> list) {
        if(list.isEmpty()) 
            this.list = list;
        else {
            this.list.addAll(list);
            Iterator<Product> it = this.list.iterator();
            for(Product p : list) {
                while(it.hasNext()) {
                    if(it.next().getId() != p.getId())
                        it.remove();
                        this.list.add(p);   
                }
            }
        }
    }
    
  • insict
    insict about 11 years
    I have equal and hashCode methods generated by eclipse, so I must use Obj.equal(Obj) style ?
  • Andrzej Doyle
    Andrzej Doyle about 11 years
    No, the LinkedHashSet will call equals itself internally, and will not insert an element that is equal to one already in the set. If you're seeing "duplicates" in your set, then the equals method must not be defined how you expect - you must have two duplicates a and b such that a.equals(b) returns false. (Or, since it's a hash-based set, a.hashCode() != b.hashCode())
  • Punith Raj
    Punith Raj about 11 years
    You can retain the default overridden hashcode implementation from eclipse, your Product class should have the logic of returning false when the productId is equal when compared with another object, and all other properties can be left unchecked for equality... Dont forget to handle transitive, reflexive and symmetric equlality for objects
  • insict
    insict about 11 years
    but how to do that when in equals method signature I must have Object type, and compiler don't know that it will be Product. I can not use generic in method signature because it won't be overridden
  • Andrzej Doyle
    Andrzej Doyle about 11 years
    @user1853125 That's how the method is defined, because you can compare equality against any object. Typically, implementations will check whether the other object is the same class - if not, they immediately return false, otherwise they cast to Product and go on to compare fields. If you have a look at start of the Eclipse-generated code, it's almost certainly doing something like this. You just need to change the logic that compares fields.
  • insict
    insict about 11 years
    ok, I wrote something like this: @Override public boolean equals(Object obj) { if(!(obj instanceof Product)) return false; Product p = (Product) obj; if(this.id.hashCode() == p.id.hashCode() ) { return true; } else { return false; } } and its works properly
  • insict
    insict about 11 years
    strange, equals works, but LinkedHashSet still adds same-id objects ;/
  • Adam Dyga
    Adam Dyga about 11 years
    @user1853125 Your equals() method is implemented wrong. You should compare object's fields, not the hashCode() result. There is a chance that completely different objects return the same hash code. You should first learn how hashtables work and why it is important to implement equals() and hashCode() properly. Irrespective of that, even if you implement equals() properly, you've got design issues in the code, which will cause other problems later
  • insict
    insict about 11 years
    so I must create Order class with simple setter and getter methods and adds overriden hashCode and equals methods?
  • insict
    insict about 11 years
    I use Product class with quantity because I use it in Datatable component with selector column, so it quite difficult to implement it by using two class.
  • Adam Dyga
    Adam Dyga about 11 years
    @user1853125 I don't fully understand what "Datatable component with selector column" is, but if you have just single table which contains both the product description, price and quantity, then the DB design is also wrong and it's probably the root of your problems. You should have one table for Product (and its descriptions) and another one for Orders (that has a foreign key to Product, and a quantity column). The Order usually has its own ID too.
  • insict
    insict about 11 years
    but when I have OrderLine class i must to create new equals and hashCode methods to define unique ID, that's really complicated