9

I know this has be discussed over and over again here, but none of the examples I've tried worked for me.

What I've got

I access the Call log from Android and I get a list of all calls made. Of course, here I get a lot of duplicates. First I make a List

List<ContactObject> lstContacts = new ArrayList<ContactObject>();

Then I add objects into it

While (get some record in call log)
{
    ContactObject contact = new ContactObject();
    contact.SetAllProperties(......)  
    lstContacts.add(contact);  
}

Set<ContactObject> unique = new LinkedHashSet<ContactObject>(lstContacts);
lstContacts = new ArrayList<ContactObject>(unique);

The Contact Object class is simple

public class ContactObject {

    public ContactObject() {
        super();
    }

 @Override
 public boolean equals(Object obj) {
     if (!(obj instanceof ContactObject))
        return false;

     return this.lstPhones == ((ContactObject) obj).getLstPhones(); 
 }

 @Override
 public int hashCode() {
     return lstPhones.hashCode();
 }

    private long Id;
    private String name;
    private List<String> lstPhones;  
    private String details;

   //... getters and settres
}

What I need

I need to have a Contact only once in the list. As I've read around here there are a couple of things that can be done like Set, HashSet, TreeSet. TreeSet seems the best as it keeps the order just as I receive it from the Call log. I've tried to make my code work with it but no success. Could anyone be so kind to give me a sample code based on my example. Thank you for your time.

The Working Solution. Thank you all for your support, you've made my day.

In ContactObject override the two methods

 @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof ContactObject))
            return false;

         return lstPhones.equals(((ContactObject) obj).getLstPhones());
     }

     @Override
     public int hashCode() {
         return (lstPhones == null) ? 0 : lstPhones.hashCode();
     }

//Getters and Setters and COnstructor....

Simply use it as

Set<ContactObject> unique = new LinkedHashSet<ContactObject>(lstContacts);
lstContacts = new ArrayList<ContactObject>(unique);
Alin
  • 14,809
  • 40
  • 129
  • 218

5 Answers5

8

Remove duplication of Custom Object

Example of Removing duplicate using Comparator

Lets suppose you have a class "Contact"

public class Contact implements Comparable<Contact> {


public String getName() {
    return this.Name;
}

public void setName(String name) {
    this.Name = name;
}

public String getNumber() {
    return this.Number;
}

public void setNumber(String number) {
    this.Number = number;
}


 ///// this method is very important you must have to implement it.
@Override
public String toString() {
    return "\n" +"Name=" + name + "   Number=" + Number;
}

Here is how you can remove duplicate entries using Set , just pass your list in the function and it will work for you. New list will be returned which will have no duplicated contacts.

 public ArrayList<Contact>  removeDuplicates(ArrayList<Contact> list){
    Set<Contact> set = new TreeSet(new Comparator<Contact>() {

        @Override
        public int compare(Contact o1, Contact o2) {
            if(o1.getNumber().equalsIgnoreCase(o2.getNumber())){
                return 0;
            }
            return 1;
        }
    });
    set.addAll(list);

    final ArrayList newList = new ArrayList(set);
    return newList;
}

It worked for me so please try and give me your feedback. Thanks

P.S: Credit goes to Nilanchala at this article

Muhammad Adil
  • 4,358
  • 3
  • 32
  • 36
8

LinkedHashSet which keeps insertion-order can be used in your case.

HashSet: no order.

TreeSet: sorted set, but not keep insertion order.

EDIT: As Software Monkey commented, hashCode() and equals() should be overwritten in ContactObject to fit the hash-based Set.

卢声远 Shengyuan Lu
  • 31,208
  • 22
  • 85
  • 130
  • Just what I was going to suggest. And if case-insensitivity is needed, the hash and equals can use a monocased (`toUpperCase`) key. – Lawrence Dol Jul 16 '11 at 07:17
  • editted: I see you are talking about a hash based set, and vsm about a Treeset. My comment is false here. – M Platvoet Jul 16 '11 at 07:48
  • @Platvoet: LinkedHashSet 'IS-A' HashSet, LinkedHashSet added doubly-linked list running through all of its entries. While TreeSet doesn't extend from HashSet. – 卢声远 Shengyuan Lu Jul 16 '11 at 08:00
  • I have updated the code... the result is the same, all duplicates are there. I compared ContactObject by their lstPhones as the Contacts are the same if they have the same lstPhones numbers. – Alin Jul 16 '11 at 12:33
  • @Alin: in method equals(): "==" just compares reference, you could try `lstPhones.equals(((ContactObject) obj).getLstPhones())` instead. And in method hashCode(): `(lstPhones == null) ? 0 : lstPhones.hashCode()` will be better. – 卢声远 Shengyuan Lu Jul 16 '11 at 14:59
3

For sure you can use TreeSet to store only once but a common mistake is do not override hashCode() and equal() methods:

This can fit for you:

 public boolean equals(Object obj) {
     if (!(obj instanceof ContactObject))
        return false;

     return this.id == ((ContactObject) obj).getId(); // you need to refine this
 }

 public int hashCode() {
     return name.hashCode();
 }
vsm
  • 3,373
  • 2
  • 25
  • 36
  • Incorrect, for a TreeSet to work, the elements should be [Comparable](http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html#TreeSet(java.util.Collection)) – M Platvoet Jul 16 '11 at 07:46
  • Your are wrong: From JAVADOC Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false. – vsm Jul 16 '11 at 07:57
  • Then we are both wrong :), They should be implemented both. Without and Comparator/Comparable a TreeSet cannot function either. – M Platvoet Jul 16 '11 at 08:02
  • Wrong, you can create a TreeSet with out a comparator. From JAVADOC A NavigableSet implementation based on a TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used. if you want to create a TreeSet with a comparator you should use TreeSet t = new TreeSet(new Comparator() { public int compare(String o1, String o2) { return 0; // compare should be done here } }); – vsm Jul 16 '11 at 08:07
  • Yes, so in this case you would need to provide a comparator/comparable since we are talking about a ContactObject. Furthermore, contains doesn't use equals and hashcode, see the implementation of [TreeMap:343](http://kickjava.com/src/java/util/TreeMap.java.htm) – M Platvoet Jul 16 '11 at 08:16
  • I'm just replying to your assertion "Without and Comparator/Comparable a TreeSet cannot function either". Why should I use contains method ? But here no comparator is needed. – vsm Jul 16 '11 at 08:24
2
List<ContactObject> listContacts = new ArrayList<ContactObject>();
//populate...

//LinkedHashSet preserves the order of the original list
Set<ContactObject> unique = new LinkedHasgSet<ContactObject>(listContacts);
listContacts = new ArrayList<ContactOjbect>(unique);
sbridges
  • 24,960
  • 4
  • 64
  • 71
  • 1
    Making everything as in your code, gives me exactly the samre listContactacs as before, with duplicates – Alin Jul 16 '11 at 12:27
1

Use Set's instead.

Set's works as an Mathematical collection, so it doesn't allow duplicated elements.

So it checks the equality and the .equals() methods for each element each time you add an new element to it.

everton
  • 7,579
  • 2
  • 29
  • 42