Java 在 TreeSet 中,基于不同属性的自定义对象的排序和唯一性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4447461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 17:37:31  来源:igfitidea点击:

In TreeSet, Sorting & Uniqueness of custom objects based on different properties

javasortingcollectionstreesetunique

提问by SmartSolution

Below is my Student class

下面是我的学生类

class Student implements Comparable {
   String name;
   int rollNo;

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

latest modification: but still no getting the right result

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

@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);
        }
    }
}

If I create object of TreeSet<Student>, I am getting sorted list of Student objects based on unique name & ordered by name also.

如果我创建 TreeSet<Student> 的对象,我将根据唯一名称和名称排序得到 Student 对象的排序列表。

But I need unique student-name in my TreeSet<Student> with order by student-rollNo.

但是我需要在我的 TreeSet<Student> 中使用唯一的学生姓名,并按 student-rollNo 排序。

Is it possible with Comparator? Can anybody help me, Every suggestion is appreciated. Thanks.

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

UPDATE: here is the complete program:

更新:这是完整的程序:

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;
    }
}

Update:2: Thank you all for your suggestions, I still need some more :)

更新: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;
    }
}

OUTPUT:

输出

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]

Friends, whatever I got by using two Comparators, Is it possible to achieve the same while adding the objects ?? I cannot first Add elements & then use new comparator to achieve the desired order.
I am manipulating thousands of values so need to consider performance also.

朋友们,无论我使用两个比较器得到什么,在添加对象时是否可以实现相同的目标??我不能先添加元素然后使用新的比较器来实现所需的顺序。
我正在处理数以千计的值,因此还需要考虑性能。

回答by Ralph

You can initialize a new TreeSet with an different comparator. - So all you have to do, is to write an new Comparator (implements java.util.Comparator interface), use this comparator to initialize the a new TreeSet and then add all students to the set.

您可以使用不同的比较器初始化新的 TreeSet。- 所以你所要做的就是编写一个新的比较器(实现 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);

Each Tree Set can have its own comparator for sorting, if no comparator is specifed, then the Tree Set uses the natural ordering of the set elements.

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

added

添加

If you need only the name uniqe Students, then you have two ways:

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

  • Implement the comparator in a way, that it returns 0 if the name of the studens is equals (but i belive this is so kinde of hack).
  • First filter the students by name, and then sort them by rollNo,
  • 以某种方式实现比较器,如果学生的名字相等,它会返回 0(但我相信这是一种 hack)。
  • 先按姓名过滤学生,然后按rollNo排序,

A bit like this:

有点像这样:

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

回答by Jigar Joshi

in TreeSetIt will use comparator while adding elements for sorting and unique check,

TreeSet添加元素进行排序和唯一检查时,它将使用比较器,

now the problem is if you use comparator for roll no you will have it sorted by roll no and unique roll nos too. you can't have both together in treeset.

现在的问题是,如果您将比较器用于卷号,您也将按卷号和唯一卷号对其进行排序。你不能在树集中同时拥有两者。

I would suggest you to go for.

我建议你去。

  1. TreeSethere you concentrate about duplicate removal
  2. then once you have unique data go for ArrayListand sort it in any order you want
  1. TreeSet在这里,您专注于重复删除
  2. 然后一旦你有独特的数据去ArrayList按你想要的任何顺序对其进行排序

回答by andersoj

Ordering

订购

The answer by @ralphon using a TreeSetwith a specified comparator is a good one, use that.

@ralph关于将 aTreeSet与指定的比较器一起使用的答案是一个很好的答案,请使用它。

Design

设计

You should wrap your concept of a "student database" inside a class that exposes and documents the correct behaviors, rather than just using a raw collection. If obtaining lists of students in particular orders is a design requirement, expose methods (perhaps returning Iterable<Student>that say that. Behind the scenes, you can do a variety of things depending on the usage pattern:

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

  • Maintain one or more Sets and or Mapssorting/indexing students by fields of interest.
  • On-demand in-place array sort using Arrays.sort()and a specified Comparator.
  • 维护一个或多个SetMaps/或按感兴趣的领域对学生进行排序/索引。
  • 使用Arrays.sort()和指定的Comparator.

Example....

例子....

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) { ... }
}

Uniqueness

唯一性

You need to override equals()and hashCode()on your Studentclass, to compare only the student name. Then you'll get uniqueness (silently) in your TreeSet. Obviously, if you do this, you need to code defensively to check to see if studentSet.contains(newStudent)before inserting newStudent, so you'll KNOW whether you've got a duplicate or not.

您需要覆盖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
   } 
}

With this in place, then your code to insert student can look like:

有了这个,你插入学生的代码看起来像:

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

   studentSet.add(toAdd);
}

Your treeset is then full of students whose names are unique, and your add operation reports a failure if not. (Throwing an exception is just one potential route, and only appropriate if adding a student with a duplicate name is ACTUALLY an exceptional condition, but you didn't say.)

然后,您的树集充满了姓名唯一的学生,如果不是,您的添加操作将报告失败。(抛出异常只是一种可能的途径,并且仅适用于添加具有重复姓名的学生实际上是一种特殊情况,但您没有说。)

回答by RamonBoza

Sorry for being to late here, here is an elegant solution:

很抱歉在这里迟到了,这是一个优雅的解决方案:

    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;
    }
}