Student.java:
package oop01;
public class Student{
private int id;
private String name;
private int age; public Student() {
} public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String toString() {
return this.id + "\t" + this.name + "\t" + this.age;
} @Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
=======================================================================================
AgeDesc.java:

package oop01;
import java.util.Comparator;
public class AgeDesc implements Comparator{
public int compare(Object o1,Object o2){
Student s1=(Student)o1;
Student s2=(Student)o2;
if(s1.getAge()==s2.getAge()){
return 0;
}
return s1.getAge()>s2.getAge()?1:-1;

}}
=======================================================================================
测试类:Demo02.java:

package oop01;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class Demo02 {
public static void main(String[] args) {
AgeDesc ad=new AgeDesc();
Set set=new TreeSet(ad);
Student s1=new Student(001,"jim",21);
Student s2=new Student(002,"tom",21);
Student s3=new Student(003,"lucy ",21);
Student s4=new Student(004,"lili",21);
Student s5=new Student(004,"lili",21);


set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);

Iterator itor=set.iterator();
while(itor.hasNext()){
System.out.println(itor.next());

}

}}
=======================================================================================
运行结果:

1    jim     21=======================================================================================
大家帮我解释一下吧,本人java基础不咋的:
我需要详细的解释,和解决方法。
TreeSet和TreeMap的源码已经看过了,知道是TreeMap中的put方法中的else块中的setValue方法惹的祸。只是现在我想找解决方法。目前比较被动的解决方法是改写AgeDesc类的compare方法:
public int compare(Object o1,Object o2){
Student s1=(Student)o1;
Student s2=(Student)o2;
if(s1.equals(s2)){
return 0;
}
return s1.getAge()>s2.getAge()?1:-1;
}

解决方案 »

  1.   

    TreeSet添加元素的时候,调用compareTo或compare方法来定位元素的位置,也就是返回compareTo或compare返回0则认为是同一个位置的元素,即相同元素
    如果想按年龄来排列,可以
    public int compare(Object o1,Object o2){
        Student s1=(Student)o1;
        Student s2=(Student)o2;
        if(s1.getAge()==s2.getAge()){ //年龄相同
            return s1.getName().compareTo(s2.getName()); //则按姓名比较
        }
        return s1.getAge()>s2.getAge()?1:-1;
    }
      

  2.   

    没有看懂我的意思,我并没有说我解决不了排序这个问题
    而是想让大家帮我解释一个事情:两个对象明显不同,为什么JDK中的TreeSet还是认为是一个对象???难道TreeSet的作者就没有考虑过这个问题吗??难道年龄一样大导致compare的结果为0,就直接调用setValue方法了???就不考虑是否是同一个对象??我的解决方法是只能自己在compare中考虑equals。疑惑的是TreeSet应该将equals的判断嵌入在他的put方法中的。
      

  3.   

    我粘上TreeSet和TreeMap中相关的源码给大家看看。
    TreeSet中的add方法:
        public boolean add(E e) {
    return m.put(e, PRESENT)==null;
        }
    TreeMap中的put方法:
     Comparator<? super K> cpr = comparator;
            if (cpr != null) {
                do {
                    parent = t;
                    cmp = cpr.compare(key, t.key);                if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else
                        return t.setValue(value);
    //这个地方是罪槐祸首
                } while (t != null);
            }
            else {
      

  4.   

    TreeSet取的是TreeMap的KeySet,是一个基于红黑树的有序集合,所以既然是有序的,那么就需要通过一个比较器(comparator)来确定两个元素的逻辑顺序。还有一个事实就是Set不允许有重复的元素。那么假如通过比较器得到的结果是0,也就是说两个元素从“顺序”上来说是一样的,那你怎么存这两个元素?你再用equals来判断这两个元素是否一样?然后在参考HashMap桶位中保存一个链表的方式来保存这两个元素吗?那么这样的数据结构还是一个Set吗?所以Java API中 compare()或者compareTo()的doc说的很清楚,约定就是:如果a.compareTo(b) == 0, 那么应该a.equals(b) == true;
    解决方法除了你改写已有的compare方法,你可以重新实现一个Comparator,然后把这个Comparator作为构造参数传递给TreeSet
      

  5.   

    这个Comparator的compare方法那就要根据你项目的需求来写:比如equals() 中用了 age,name, 那么compare也比较age,name.
    我觉得你的Student直接Comparable接口,然后compareTo()方法按照API的约定实现,这样比较好。
    其他地方如果需要不同的排序算法,那么你在重新实现就是。我觉得String类就是一个比较好的例子,本身实现了Comparable接口,而且同时实现了一个CaseInsensitiveComparator提供compareToIgnoreCase()方法。
      

  6.   

    不是没动你的意思,而是你没懂我的意思
    TreeSet调用的是compareTo或compare方法来比较元素,然后用比较结果来排序,这样,排在同一个位置的就被看成是同一个元素,否则就不因该排在同一个位置,所以并不是单纯地用equals和hashCode来判断是不是同一个元素。TreeSet底层就是这么实现的,这也正是特殊的地方,所以需要特别注意。在javadoc里有明确地说明了这一点
    一下是javadoc里的说明,注意红色部分public class TreeSet<E>extends AbstractSet<E>implements NavigableSet<E>, Cloneable, Serializable基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。 此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。 注意,如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator。)这是因为 Set 接口是按照 equals 操作定义的,但 TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也是 定义良好的;它只是违背了 Set 接口的常规协定。 注意,此实现不是同步的。如果多个线程同时访问一个 TreeSet,而其中至少一个线程修改了该 set,那么它必须 外部同步。这一般是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSortedSet 方法来“包装”该 set。此操作最好在创建时进行,以防止对 set 的意外非同步访问:    SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果从结构上对 set 进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出 ConcurrentModificationException。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。 注意,迭代器的快速失败行为无法得到保证,一般来说,存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。 此类是 Java Collections Framework 的成员。
      

  7.   

    LZ可以在自己的代码的compareTo或compare方法里打印一些相应的信息,其他的equals或hashCode也打印一些信息,然后调用TreeSet的add方法看看,到底是哪个方法会打印出信息,这样你就知道TreeSet会调用什么方法来比较判断两个元素是否相同了。
      

  8.   

    不是不知道TreeSet的排序之类的问题,而是搞不懂为什么TreeSet的排序不涉及到CompareTo方法。
      

  9.   

    我说错了,我的意思是,我不清楚为什么TreeSet的设计人员在设计add方法时,只考虑compareTo方法的返回值,而不去考虑equals或hashcode。
    例如:两个Person,他们的身份证号、地址、身高等都不同。唯独只有年龄相同。此时,compareTo内部实现时出现了:if(this.age == person.age ){return 0 ;}   --> 那么当两个完全不同的person,只要它们的年龄相同,则认为两个person是同一个person,所以第二个person不会add成功。why?不符合常理啊。