在TreeSet,Sorting&基于不同属性的自定义对象的唯一性

时间:2010-12-15 06:58:41

标签: java sorting collections treeset unique

以下是我的学生班

class Student implements Comparable {
   String name;
   int rollNo;

   @Override
   public int compareTo(Object obj) {
        return ((Student)obj).name.compareTo(this.name);
   }
} 

最新修改:但仍然没有得到正确的结果

@Override
public int compareTo(Object obj) {
    Student s = (Student) obj;
    if (name.equals(s.name)) { // achieving uniqueness
        return 0;
    } else {
        if (rollNo < s.rollNo) {
            return -1;
        } else if (rollNo > s.rollNo) {
            return 1;
        } else {
            // this makes `name` the second ordering option.
            // names don't equal here
            return name.compareTo(s.name);
        }
    }
}

如果我创建了TreeSet&lt; Student&gt;的对象,我将根据唯一名称&amp;获取Student对象的排序列表。按名称排序。

但我需要在TreeSet中使用唯一的学生姓名&lt; Student&gt;由student-rollNo订购。

比较器可以吗?任何人都可以帮助我,每个建议都表示赞赏。 感谢。

更新:这是完整的程序:

public class Student implements Comparable {

    int rollNo;
    String name;

    Student(String n,int rno) {
        rollNo=rno;
        name=n;
    }

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

        TreeSet<Student> ts = new TreeSet<Student>();
        ts.add(new Student("bbb",2));
        ts.add(new Student("aaa",4));
        ts.add(new Student("bbb",2));
        ts.add(new Student("ccc",3));
        ts.add(new Student("aaa",1));
        ts.add(new Student("bbb",2));
        ts.add(new Student("bbb",5));

        System.out.println(ts);

    }

    @Override
    public int compareTo(Object obj) {
        Student s = (Student) obj;
        if (name.equals(s.name)) { // achieving uniqueness
            return 0;
        } else {
            if (rollNo < s.rollNo) {
                return -1;
            } else if (rollNo > s.rollNo) {
                return 1;
            } else {
                // this makes `name` the second ordering option.
                // names don't equal here
                return name.compareTo(s.name);
            }
        }
    }

    @Override
    public String toString() {
        return name + rollNo;
    }
}

更新:2:谢谢大家的建议,我还需要更多:)



/*
 * Actual scenario is having different properties,
 * So here I am just relating my actual scenario with Student class
 */
class Student implements Comparable {
    // sorting required on rollNo
    int rollNo;
    // Unique name is required
    String name;

    Student(String n, int rno) {
        rollNo = rno;
        name = n;
    }

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

        TreeSet<Student> tsName = new TreeSet<Student>();
        // here by default, order & uniqueness by name only
        tsName.add(new Student("ccc", 2));
        tsName.add(new Student("aaa", 4));
        tsName.add(new Student("ddd", 1));
        tsName.add(new Student("bbb", 3));
        tsName.add(new Student("ddd", 5));
        // output: aaa:4, bbb:3, ccc:2, ddd:1
        System.out.println(tsName);

        // creating new comparator for student RollNo
        TreeSet<Student> tsRollNo = new TreeSet<Student>(new Comparator<Student>() {
                    public int compare(Student stud1, Student stud2) {
                        return new Integer(stud1.rollNo).compareTo(stud2.rollNo);
                    }
                });
        tsRollNo.addAll(tsName);
        System.out.println(tsRollNo);
        // now got the desire output: ddd:1, ccc:2, bbb:3, aaa:4
    }

    public boolean equals(Object obj) {
        // internally not used to check equality while adding objects
        // in TreeSet
        System.out.println("equals() for " + this + " & " + ((Student) obj));
        return false;// return false/true doesn't make any sense here
    }

    @Override
    public int compareTo(Object obj) {
        Student s = (Student) obj;
        // internally inside TreeSet, compareTo is used to decide
        // whether two objects are equal or not,
        // i.e. compareTo will return 0 for same object(here student name)
        System.out.println("compareTo() for " + this + " & " + ((Student) obj));
        // achieving uniqueness
        return name.compareTo(s.name);
    }

    @Override
    public String toString() {
        return name + ":" + rollNo;
    }
}

输出

compareTo() for aaa:4 & ccc:2
compareTo() for ddd:1 & ccc:2
compareTo() for bbb:3 & ccc:2
compareTo() for bbb:3 & aaa:4
compareTo() for ddd:5 & ccc:2
compareTo() for ddd:5 & ddd:1
[aaa:4, bbb:3, ccc:2, ddd:1]
[ddd:1, ccc:2, bbb:3, aaa:4]

朋友,无论我使用两个比较器得到什么,是否有可能 添加对象时实现相同的?? 我不能先添加元素&amp;然后使用新的比较器来实现所需的顺序。 我正在操纵成千上万的价值观,所以也需要考虑表现。

4 个答案:

答案 0 :(得分:5)

in TreeSet它将使用比较器,同时添加用于排序和唯一检查的元素,

现在的问题是,如果你使用比较器进行滚动,你就会按照滚动编号和唯一滚动nos进行排序。你不能把两者放在一起。

我建议你去。

  1. TreeSet在这里您专注于重复删除
  2. 然后,一旦您拥有ArrayList的唯一数据,并按您想要的任何顺序对其进行排序

答案 1 :(得分:4)

订购

使用带有指定比较器的TreeSet的{​​{3}}是一个好的,使用它。

设计

您应该将一个“学生数据库”的概念包含在一个暴露和记录正确行为的类中,而不仅仅是使用原始集合。如果获得特定订单的学生列表是一项设计要求,请公开方法(可能会返回Iterable<Student>这样的说法。在幕后,您可以根据使用模式做各种事情:

  • 按照感兴趣的字段维护一个或多个Set和/或Maps排序/索引学生。
  • 使用Arrays.sort()和指定Comparator的按需就地数组排序。

实施例...

final class StudentTable {
   private static final Comparator<Student> studentRollNoComparator = ...;
   private final SortedSet<Student> sortedByRollNo = 
      new TreeSet<Student>(studentRollNoComparator);

   public Iterable<Student> studentsOrderedByRollNo()
   {
      return sortedByRollNo;
   } 

   //see below
   public void addStudent(final Student foo) { ... }
}

唯一

您需要覆盖equals()课程中的hashCode()Student,才能仅比较学生姓名。然后,您将在TreeSet中获得唯一性(默默地)。显然,如果你这样做,你需要在插入studentSet.contains(newStudent)之前进行防御性编码以检查是否newStudent,这样你就知道你是否有重复。

final class Student implements Comparable {
   ...

   @Override
   public boolean equals(Object o)
   {
      return o!=null &&  
             o (instanceof Student) &&
             ((Student)o).name.equals(this.name);
   }

   @Override
   public int hashCode()
   {
      return name.hashCode();  // good enough for this purpose
   } 
}

有了这个,那么插入学生的代码可能如下所示:

void addNewStudent(final Student toAdd)
{
   if (studentSet.contains(toAdd)) { 
      throw new IllegalStateException("Student with same name as "+toAdd+" already exists.");
   }

   studentSet.add(toAdd);
}

您的树集中充满了名称唯一的学生,如果没有,您的添加操作会报告失败。 (抛出异常仅仅是一条潜在的路线,只有在添加具有重复名称的学生时才适用,这是一个例外情况,但你没有说。)

答案 2 :(得分:1)

您可以使用不同的比较器初始化新的TreeSet。 - 所以你要做的就是编写一个新的Comparator(实现java.util.Comparator接口),使用这个比较器初始化一个新的TreeSet,然后将所有学生添加到集合中。

TreeSet<Student> sortedByRollNo new TreeSet<Student>(new RollNoComparator());
sortedByRollNo.addAll(allStudents);

TreeSet<Student> sortedByY new TreeSet<Student>(new YComparator());
sortedByY.addAll(allStudents);

每个树集可以有自己的比较器进行排序,如果没有指定比较器,则树集使用集合元素的自然顺序。

已添加

如果您只需要名称uniqe Students,那么您有两种方式:

  • 以某种方式实现比较器,如果studens的名称相等则返回0(但我相信这是hack的kinde)。
  • 首先按名称过滤学生,然后按rollNo
  • 对其进行排序

有点像这样:

TreeSet<Student> sortedByRollNo new TreeSet<Student>(new RollNoComparator());
sortedByRollNo.addAll(new TreeSet<Student>(allStudends)); //this uses the native comparator to filter by uniqe name

答案 3 :(得分:0)

对不起,这是一个优雅的解决方案:

    public class OwnSortedList<T> extends TreeSet<T> {

    private static final long serialVersionUID = 7109828721678745520L;


    public OwnSortedList(Comparator<T> levelScoreComparator) {
        super(levelScoreComparator);
    }


    public boolean add(T e) {
        boolean existsElement = false;

        Iterator<T> it = iterator();
        while(it.hasNext() && !existsElement){
            T nextElement = it.next();
            if(nextElement.equals(e)){
                // Element found
                existsElement = true;
                Comparator<? super T> comparator = comparator();
                int compare = comparator.compare(nextElement, e);
                if(compare > 0){
                    remove(nextElement);
                    super.add(e);
                    //element added so return true
                    return true;
                }

            }
        }

        if(!existsElement){
            super.add(e);
        }

        return false;
    }
}