3

i have a java class like this

public class A {

    private String field1;
    private String field2;

    // getters, setters but no equals and hashcode
}

and a list of objects of this class, i want to remove from this list all the duplicates elements that has the same field1 or the same field2, so i have 2 Comparators

public class Comparator1 implements Comparator<A> {
    public int compare(A o1, A o2) {

        return o1.getField1().compareToIgnoreCase( o2.getField1() );
    }
}

public class Comparator2 implements Comparator<A> {
    public int compare(A o1, A o2) {

        return o1.getField2().compareToIgnoreCase(o2.getField2());
    }
}

so to do the task i use treeset like

TreeSet<A> ts1 = new TreeSet<A>(new Comparator1())
ts1.addAll(list)

TreeSet<A> ts2 = new TreeSet<A>(new Comparator2())
ts2.addAll(ts1)

list.clear()
list.addAll(ts2)

but how can i do the same using just one comparator and one treeset ?

Thanks for the help

Update:

Thanks all for the answers, but after reading them i don't know if this is the right approach to the real problem.

In my real case field1 is like a phone number and field2 is like a name. So i don't want to call the same phone number more than one time (this is the first treeset to removes duplicates) and i don't want to call more than one time the same name (the second treeset to removes duplicates)

You can modify the class but i'd like to know if this approach is ok to resolve the real problem.

If this approach is correct, from your question, i see that without modifying the class is not possible to use just one comparator

Thanks

res1
  • 3,482
  • 5
  • 29
  • 50

6 Answers6

2

You can't use one comparator to sort by two criteria at the same time, so there is no real way to go better than two TreeSets in your case. Of course, you can wrap them in one data structure.

(Alternatively you could use two HashMaps, each having one of the strings as key - this will be faster on average, but is more complicated to program.)

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
1

You can't, and it's not clear to me that what you're trying to do is well-defined.

Are you aware that your current approach depends both on the order in which elements are added and on whether you check field1 or field2 first for duplicates? Imagine you had these objects of class A:

A ab = new A("a", "b");
A cb = new A("c", "b");
A cd = new A("c", "d");

Checking field1 first gives the result [ab] or [ab, cd], depending on the order added.

Checking field2 first gives the result [cb] or [ab, cd], depending on the order added.

This is pretty strange behavior. Is this what you intended? I don't think it is possible to reproduce this with a single TreeSet and Comparator in the general case.

rlibby
  • 5,931
  • 20
  • 25
  • @res1, your update doesn't clarify the problem. Fine pretend "a" is a name and "c" is a name and "b" is a phone number and "d" is a phone number. Sometimes with these three pairs you will only call "a" at "b", never calling "c" at all, and never calling the number "d" at all. Is that what you intended? If not, please clarify what you want to happen in that situation. – rlibby Apr 09 '11 at 02:06
  • looking at your test case this is not what i want, i want to get the [ab, cd] as result and not the [ab]. – res1 Apr 09 '11 at 02:34
0

If your intention is to do two levels of sorting(first: PhoneNumber and second:Name), then you can use the following code, where the duplicate check will be done against both the fields(field1 and field2). As we are already using compareTo for both the fields, it is not required to use equals and hashcode. But it is always good practice to use hashcode and equals.

public class A implements Comparable<A> {

private String field1;
private String field2;

public A(String number, String name) {
    this.field1 = number;
    this.field2 = name;
}

// First level sorting will be done by field1. 
// If field1 is equal then second level sorting will be done on field2
@Override
public int compareTo(A o) {
    int compareTo = field1.compareTo(o.getNumber());
    if(compareTo==0){
        return field2.compareTo(o.getName());
    }
    return compareTo;
}

public String getNumber() {
    return field1;
}

public String getName() {
    return field2;
}

}

Jagadeesh
  • 2,730
  • 6
  • 30
  • 45
0
public class RemoveDuplicate {

public static void main(String[] args) {

    final ArrayList<Student> students = new ArrayList<Student>();

    Set<Student> set = new TreeSet<Student>();
    Student[] starr = new Student[6];
    starr[0] = new Student("Student1", "1005");
    starr[1] = new Student("Student2", "1004");
    starr[2] = new Student("Student3", "1003");
    starr[3] = new Student("Student6", "1002");
    starr[4] = new Student("Student5", "1001");
    starr[5] = new Student("Student6", "1000");

    Arrays.sort(starr, Student.StudentIdComparator);
    for (Student s : starr) {
        students.add(s);
    }

    System.out.println(students);
    set.addAll(students);

    System.out.println("\n***** After removing duplicates *******\n");

    final ArrayList<Student> newList = new ArrayList<Student>(set);

    /** Printing original list **/
    System.out.println(newList);
}}
0
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.Comparator;
 import java.util.List;

 public class RemoveDuplicate {

public static void main(String[] args) {
    Set<Student> set = new TreeSet<Student>();
    List<Student> students = Arrays.asList(new Student("Student1", "1005"), new Student("Student2", "1004"),
            new Student("Student3", "1003"), new Student("Student6", "1002"), new Student("Student5", "1001"),
            new Student("Student6", "1000"));

    // Sorting Using Lambda

    students.sort(new Comparator<Student>() {

        @Override
        public int compare(Student s1, Student s2) {

            return s1.getId().compareTo(s2.getId());
        }

    });

    System.out.println(students);
    set.addAll(students);

    System.out.println("\n***** After removing duplicates *******\n");

    final ArrayList<Student> newList = new ArrayList<Student>(set);

    /** Printing original list **/
    System.out.println(newList);
}

  }

 class Student implements Comparable<Student> {
private String name;
private String id;

public Student(String name, String id) {
    this.name = name;
    this.id = id;
}

public String getName() {
    return name;
}

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

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

@Override
public String toString() {
    return "\n" + "Name=" + name + "   Id=" + id;
}

@Override
public int compareTo(Student o1) {
    if (o1.getName().equalsIgnoreCase(this.name)) {
        return 0;
    }
    return 1;
    }

// public static Comparator<Student> StudentIdComparator = (Student
// s1,Student s2) -> s1.getId().compareTo(s2.getId());
   }
0
public static <A extends Comparable<?>>  TreeSet<A> getTreeSet(Collection<A> list){
    TreeSet<A> result = new TreeSet<A>();
    HashSet<A> unique = new HashSet<A>();
    unique.addAll(list);
    result.addAll(unique);
    return result;
}

Generic function that adds items to hashset to make them unique, and then drop them to TreeSet to sort. You can use it with: TreeSet<A> ts1 = getTreeSet(list);.

This approach works well for a fixed list.

@BalusC No, this assumes

public class A implements Comparable<A> {

    private String field1;
    private String field2;

    @Override
    public int compareTo(A o) {
        // No null checks, because it's illegal anyways.
        int tmp = 0;
        if ((tmp = field1.compareToIgnoreCase(o.field1)) != 0)
            return tmp;
        if ((tmp = field2.compareToIgnoreCase(o.field2)) != 0)
            return tmp;
        return tmp;
    }
    // getters, setters but no equals and hashcode
}
Margus
  • 19,694
  • 14
  • 55
  • 103
  • This assumes that `equals()` and `hashCode()` is implemented on `A`. – BalusC Apr 09 '11 at 00:52
  • @BalusC I wounder how you have deciphered : `A extends Comparable>`. – Margus Apr 09 '11 at 01:00
  • Ah I missed that part. Well, I understood that modifying `A` is not an option. I only don't understand how the `HashSet` is necessary in this example. – BalusC Apr 09 '11 at 01:06
  • `A` is not declared as `final`, so he can `extend` A and use it instead. – Margus Apr 09 '11 at 01:21
  • Extending `A` involves more changes in remnant of the code using the class. If it's also internally used by some 3rd party API, then it might not be a viable option. – BalusC Apr 09 '11 at 01:22
  • I do not play games with if's. That detail is not mentioned, so I can ignore it. – Margus Apr 09 '11 at 01:24
  • That's exactly why I posted a comment on OP's question in order to have more clarification before wasting time in posting answers which might not suit the *real needs* at all ;) – BalusC Apr 09 '11 at 01:26