1

I have a nice problem - create a phone book - containing list of contacts. As a phonebook goes,

  1. Contacts are to be always sorted.(by name)
  2. Can star mark certain contacts, so they have to be above all the rest.(the * contacts are ordered by the time of contact creation)

    class PhoneBook{
    //require an always sorted d.s
    TreeSet<Contact> contacts = new TreeSet<Contact>();
    
    @Override
    public String toString() {
        return "PhoneBook [contacts=" + contacts + "]";
    }
    
    public boolean addContact(Contact contact){
        //validate before adding the contact.
        return contacts.add(contact);
    }
    

    }

    class Contact implements Comparable<Contact>{
    String name;
    int phoneNo;
    Date timeAdded;
    boolean starContact;
    
    
    
    public Contact(String name, int phoneNo, Date timeAdded, boolean starContact) {
        super();
        this.name = name;
        this.phoneNo = phoneNo;
        this.timeAdded = timeAdded;
        this.starContact = starContact;
    }
    
    
    
    @Override
    public int compareTo(Contact otherContact) {
        if(this.starContact && otherContact.starContact){
            return this.timeAdded.before(otherContact.timeAdded)?-1:1; //impossible to add 2 contacts at the same time
        }else if(this.starContact){
            return -1;
        }else if(otherContact.starContact){
            return 1;
        }else{
            //simple Contacts
            return this.name.compareTo(otherContact.name);
        }
    }
    
    
    
    
    
    
    @Override
    public String toString() {
        return "\nContact [name=" + name + ", timeAdded=" + timeAdded
                + ", starContact=" + starContact + "]";
    }
    
    
    }
    

Test Code

    public class MobilePhoneBookDemo {

/**
 * @param args
 */
public static void main(String[] args) {

    PhoneBook phoneBook = new PhoneBook();

    Contact frnd1 = new Contact("Z",56,new Date(),false);
    phoneBook.addContact(frnd1);
    Contact frnd2 = new Contact("A",3,new Date(),false);
    phoneBook.addContact(frnd2);
    Contact frnd3 = new Contact("C",30,new Date(),false);
    phoneBook.addContact(frnd3);
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Contact ta = new Contact("Ta", 5, new Date(), true);
    phoneBook.addContact(ta);
    Contact ma = new Contact("Ma", 31, new Date(), true);
    phoneBook.addContact(ma);
    Contact baba = new Contact("Baba", 300, new Date(), true);
    phoneBook.addContact(baba);

    //change the priority later for one of my friends.
    System.out.println(phoneBook);
    frnd1.starContact = true;
    System.out.println(phoneBook.contacts.contains(frnd1));

    if(phoneBook.contacts.remove(frnd1)){
        System.out.println("removed");
        phoneBook.contacts.add(frnd1);
    }

    System.out.println(phoneBook);
}

}

Problems faced:

  1. The contains doesn't find the entry anymore, what's amiss? I did try and put an equals and a hashcode on Contact, apparently, if there is a Comparator/Comparable present, the compare* is only invoked.
  2. Is it fair to use a TreeSet here, or should any other datastructure be used? For eg. HashSet and then convert to a TreeSet?
  3. The contains() doesn't even compare for all entries in the map, it just compared against C,Ma and Ta entries. Why was that?

Questions priority according to order. I appreciate all the answers, but this is indeed a complete test case, so please try and run PhoneBook just once before providing an answer. Thanks a lot.

Achow
  • 8,600
  • 6
  • 39
  • 49
  • 1
    If contacts must be always sorted, use a Treeset – JohnJohnGa Jan 17 '13 at 09:30
  • TreeMap or TreeMap,Contact>.If its the former, then its internally done by the TreeSet, if its the latter, then, how does it help? – Achow Jan 17 '13 at 09:33
  • It was in response to the earlier comment, where it was mentioned to use a TreeMap, however, it is now changed to a TreeSet. – Achow Jan 17 '13 at 09:36

3 Answers3

4

This line:

return this.timeAdded.before(otherContact.timeAdded)?-1:1;

will never return 0 if you compare a contact with itself. Therefore the set will not be able to find the object with contains()

András Kerekes
  • 1,001
  • 7
  • 13
  • Really? So compareTo() will compare to itself? I almost repped this answer without testing, as it looks so good, but it actually doesn't work. The contain() never invoked compareTo() with itself. – Achow Jan 17 '13 at 09:41
  • 1
    oh downvoter,care to explain? I think SO should mandate all downvoters to atleast leave a comment, so that the downvoter owns responsibility. – Achow Jan 17 '13 at 09:45
  • 1
    Looking at the code of `TreeMap.getKey()` (where `TreeSet.contains()` ends up) it uses the comparator to find the object you are looking for. So if the set contains `frnd1`, the call to `set.contains(frnd1)` will return the object that gives 0 for the 'compareTo()'. Which in this case will be none of the entries, thus returns `null` – András Kerekes Jan 17 '13 at 10:14
  • @anirbanchowdhury You are mistaken. It invokes compareTo() to binary-search the tree until it returns zero when comparing the search key to the entry keys. – user207421 Jan 17 '13 at 10:37
  • @EJP , yes my mistake, the compareTo() may compare it to itself, but NOT always, if u doubt me, do debug once just to check it.However, this doesn't solve the question. – Achow Jan 18 '13 at 08:05
1

You had me at

contacts are to be always sorted.(by name)

If order is important use TreeSet. Elements are stored in a variety of binary search tree, this means that they are sorted by default.

HashSet on the other hand, does not guarantee any order, sorting or even insert order.


Edit - Try to write your conditions in this way, more readable and more robust as well. The order of conditions is the order of priority of comparison.

if(a != b){
    return a<b;
}

if(c != d) {
    return c<d;
}

//and so on.

return 0;
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • @anirbanchowdhury could you point to the reference which says `equals` wont work? "if there is a Comparator/Comparable present, the compare* is only invoked." – Karthik T Jan 17 '13 at 09:53
  • Do check out the contains() on the TreeSet and where it leads to. Would lead to getEntry() on the TreeMap. – Achow Jan 17 '13 at 10:00
  • [You are right](http://stackoverflow.com/questions/3501997/why-doesnt-treeset-contains-work) In which case, the problem pointed out by Anras Kerekes is very valid, but perhaps not the whole story. – Karthik T Jan 17 '13 at 10:03
  • @anirbanchowdhury "The contains() doesn't even compare for all entries in the map," How do you infer that. – Karthik T Jan 17 '13 at 10:07
  • @anirbanchowdhury Infact the problem suggested by Anras is probably the main one, since frnd1 is a star contact. – Karthik T Jan 17 '13 at 10:08
  • @anirbanchowdhury and depending on the granularity of Date, it is probably easy to create 2 Contacts with same name – Karthik T Jan 17 '13 at 10:10
  • 1. I debugged 2. No its not, reasons mentioned in comments to his answer. It looks good, but its actually not. 3. Frankly, this was just a POC to think of a solution.I agree to your point, but doesn't answer my questions. – Achow Jan 17 '13 at 10:15
  • @anirbanchowdhury Im sorry but I need to leave for home now, Ill try to get back to you once I get there. – Karthik T Jan 17 '13 at 10:18
0

The solution is simple, first remove, then change the value and add it back again. I comment the modification first,I have also put in the modified compareTo() since there were lots of confusion around it.

//frnd1.starContact = true;
    System.out.println("Entry present>"+phoneBook.contacts.contains(frnd1));

    if(phoneBook.contacts.remove(frnd1)){
        System.out.println("removed");
        frnd1.starContact = true;
        phoneBook.contacts.add(frnd1);
    }


@Override
public int compareTo(Contact otherContact) {
    if(otherContact.phoneNo == this.phoneNo){
        return 0;
    }
    if(this.starContact && otherContact.starContact){
        return this.timeAdded.before(otherContact.timeAdded)?-1:1; //impossible to add 2 contacts at the same time
    }else if(this.starContact){
        return -1;
    }else if(otherContact.starContact){
        return 1;
    }else{
        //simple Contacts
        return this.name.compareTo(otherContact.name);
    }
}
Achow
  • 8,600
  • 6
  • 39
  • 49