A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 郭军亮 中级黑马   /  2013-4-17 22:01  /  2492 人查看  /  12 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 郭军亮 于 2013-4-19 22:00 编辑

我想问一下为什么用TreeSet时,类要来实现排序的时候要定义HashCode()方法?还有TreeSet是怎样调用compareTo()方法和equals()方法的?例如程序:
import java.io.*;
import java.util.*;

class Student implements Comparable<Student>
{
        private String name;
        private int ma,cn,en;
        private int sum;

        Student(String name,int ma,int cn,int en)
        {
                this.name = name;
                this.ma = ma;
                this.cn = cn;
                this.en = en;
                sum = ma + cn + en;
        }


        public int compareTo(Student s)
        {
                int num = new Integer(this.sum).compareTo(new Integer(s.sum));
                if(num==0)
                        return this.name.compareTo(s.name);
                return num;
        }



        public String getName()
        {
                return name;
        }
        public int getSum()
        {
                return sum;
        }

        public int hashCode()
        {
                return name.hashCode()+sum*78;

        }
        public boolean equals(Object obj)
        {
                if(!(obj instanceof Student))
                        throw new ClassCastException("类型不匹配");
                Student s = (Student)obj;

                return this.name.equals(s.name) && this.sum==s.sum;
        }

        public String toString()
        {
                return "student["+name+", "+ma+", "+cn+", "+en+"]";
        }
}

class StudentInfoTool
{
        public static Set<Student> getStudents()throws IOException
        {
                return getStudents(null);
        }

        public static Set<Student> getStudents(Comparator<Student> cmp)throws IOException
        {
                BufferedReader bufr =
                        new BufferedReader(new InputStreamReader(System.in));

                String line = null;
               
                Set<Student> stus  = null;
                if(cmp==null)
                        stus = new TreeSet<Student>();
                else
                        stus = new TreeSet<Student>(cmp);
                while((line=bufr.readLine())!=null)
                {
                        if("over".equals(line))
                                break;
                        
                        String[] info = line.split(",");
                        
                        Student stu = new Student(info[0],Integer.parseInt(info[1]),
                                                                                Integer.parseInt(info[2]),
                                                                                Integer.parseInt(info[3]));

                        
                        stus.add(stu);
                }

                bufr.close();

                return stus;
        }

        public static void write2File(Set<Student> stus)throws IOException
        {
                BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));

                for(Student stu : stus)
                {
                        bufw.write(stu.toString()+"\t");
                        bufw.write(stu.getSum()+"");
                        bufw.newLine();
                        bufw.flush();
                }

                bufw.close();

        }
}



class StudentInfoTest
{
        public static void main(String[] args) throws IOException
        {

                Comparator<Student> cmp = Collections.reverseOrder();

                Set<Student> stus = StudentInfoTool.getStudents(cmp);

                StudentInfoTool.write2File(stus);
        }
}

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

12 个回复

倒序浏览
1,当集合底层采用的是hash表的存储结构用于存储自定义对象时才有必要覆写hashCode(),equals()方法,如往HashSet里面存入一个元素时,先计算其hash值,如该hash值上已存在元素,再调用equals方法,如果equals为true值元素插入失败,否则插入元素成功..如HashSet,HashMap,
2.而对于TreeSet其底层采用的是二叉树,其会对存储对象进行排序,所以存入元素需具有比较性,自定义对象实现Comparable接口,每次往TreeSet里面存入一个对象时会与前面对象进行比较(调用compareTo()方法),从而实现元素的排序功能
个人见解..

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

回复 使用道具 举报
谢谢一楼的,但是我还是郁闷TreeSet()是怎么调用这些方法的啊?比如那个Hashcode()compareTo()和equal是()等的方法
回复 使用道具 举报
学生对象实现了Comparable,重写了CompareTo()方法
HashCode() 与equals() 方法在Object类中存在
回复 使用道具 举报
哦   但是TreeSet()是怎么调用这些方法的啊?
回复 使用道具 举报
郭军亮 发表于 2013-4-18 08:49
谢谢一楼的,但是我还是郁闷TreeSet()是怎么调用这些方法的啊?比如那个Hashcode()compareTo()和equa ...

在equals方法中compareTo方法中加入打印语句,一看就知道了。。他是会自动调用的
因为TreeSet集合是二叉树结构,每次往集合里面放一个元素,都得和集合里面的元素比一下,这时候就要调用
compareTo方法和equals方法;
回复 使用道具 举报
Miss小强 发表于 2013-4-18 22:19
在equals方法中compareTo方法中加入打印语句,一看就知道了。。他是会自动调用的
因为TreeSet集合是二叉 ...

不过貌似测试了下equals方法没有执行啊。。。
回复 使用道具 举报
你错误理解了TreeSet。和HashSet

TreeSet仅仅调用存储对象的compareTo(),这也是为什么一个TreeSet中的对象要实现Comparable的原因
记住,对于底层是二叉树结构的TreeSet来说,equals和hashCode方法对它来说没有用,仅仅是在用到底层数据结构是哈希表的时候,才使用这两个方法

当然,你也可以不去指定一个存储对象中实现实现Comparable,但是在创建TreeSet的时候,要指定一个比较器
也就是创建一个实现Comparator的对象。这样在存入的时候就去调用Comparator中的compare方法了

给你两个例子。你体会一下

public class Test {

        /**
         * @param args
         */
       
        public  Test(){
       
        }
       

        public static void main(String[] args) throws Exception
        {
                TreeSet<Test1> ts = new TreeSet<Test1>();
                ts.add(new Test1());
                ts.add(new Test1());
               
        }

}
class Test1 implements Comparable<Test1>
{
        @Override
        public int compareTo(Test1 o) {
                // TODO Auto-generated method stub
                System.out.println("compareTo被调用");
                return 0;
        }
        @Override
        public int hashCode() {
                // TODO Auto-generated method stub
                System.out.println("hashCode被调用");
                return super.hashCode();
        }
        @Override
        public boolean equals(Object obj) {
                // TODO Auto-generated method stub
                System.out.println("equals被调用");
                return super.equals(obj);
        }
       
}
证明了仅仅调用了compareTo方法



当你没有实现Comparable接口的时候

会出现
Exception in thread "main" java.lang.ClassCastException: day9.Test1 cannot be cast to java.lang.Comparable
        at java.util.TreeMap.compare(TreeMap.java:1188)
        at java.util.TreeMap.put(TreeMap.java:531)
        at java.util.TreeSet.add(TreeSet.java:255)
        at day9.Test.main(Test.java:17)
异常。。。

但是,当你指定一个比较器的时候

public class Test {

        /**
         * @param args
         */
       
        public  Test(){
       
        }
       

        public static void main(String[] args) throws Exception
        {
                TreeSet<Test1> ts = new TreeSet<Test1>(new Mycom());
                ts.add(new Test1());
                ts.add(new Test1());
               
        }

}

class Mycom implements Comparator<Test1>
{
@Override
        public int compare(Test1 o1, Test1 o2) {
                // TODO Auto-generated method stub\
        System.out.println("compare被调用");
                return 0;
        }       
        }


class Test1
{
       
        @Override
        public int hashCode() {
                // TODO Auto-generated method stub
                System.out.println("hashCode被调用");
                return super.hashCode();
        }
        @Override
        public boolean equals(Object obj) {
                // TODO Auto-generated method stub
                System.out.println("equals被调用");
                return super.equals(obj);
        }
       
}
就会显示compare方法被调用2次

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

回复 使用道具 举报
黄玉昆 黑马帝 2013-4-19 07:16:28
9#
如果问题未解决,请继续追问,如果问题解决了,请将问题分类改为“已解决”,谢谢
回复 使用道具 举报
我想问一下  treeSet()什么情况下调用那个HashCode()函数啊?
回复 使用道具 举报
本帖最后由 先小涛 于 2013-4-19 15:43 编辑

当你调用HashSet的add方法时,他会调用map中的put方法,在put方法中会调用hashCode和equals方法,看源代码红字部分
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}
当你调用TreeSet中的add方法时,add方法则调用map中的put方法,在put方法里则根据来比较,源代码如下
public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {
        root = new Entry<K,V>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    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 {
        if (key == null)
            throw new NullPointerException();
        Comparable<? super K> k = (Comparable<? super K>) key;
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    Entry<K,V> e = new Entry<K,V>(key, value, parent);
    if (cmp < 0)
        parent.left = e;
    else
        parent.right = e;
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}

评分

参与人数 1技术分 +1 收起 理由
田磊阳 + 1

查看全部评分

回复 使用道具 举报
同学你好 有什么不对的请指出 共同提高:

TreeSet用的是比较器comparable和comparator.
在默认情况下TreeSet是有调用equals方法的。若我们复写了就另当别论了。

这是Comparator的源码,我吧注释去掉了。
package java.util;

public interface Comparator<T>
{
    int compare(T o1, T o2);
    boolean equals(Object obj);//默认情况会调用equals
}

这是Comparable的源码:

package java.lang;
import java.util.*;

public interface Comparable<T>
{
    public int compareTo(T o);
}

评分

参与人数 1技术分 +1 收起 理由
田磊阳 + 1

查看全部评分

回复 使用道具 举报
谢谢  好像有点儿懂了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马