比较器是怎么参与到建立TreeSet“集合”对象过程中

BZHeiZ 2015-09-22 04:53:20
ComparatorByName这个代码我写出来,可是我却不知道ComparatorByName对象作为构造函数参数是怎么参与到建立TreeSet“集合”对象过程中的!

“Comparable接口和Comparator接口的比较”,这是倾尽所能对Comparator接口的compare的一种猜想(如箭头所指),不知道对不对?(每次我试图想去理解这类问题,总是靠猜!)

ComparatorByName
package com.itheima.comparator;

import java.util.Comparator;

import com.itheima.domain.Student;
/**
* 自定义一个比较器,用来对学生对象按照姓名排序。
* @author Roger
*Comparable接口和Comparator接口的比较: <————————————
* o1就相当于Comparable接口的compareTo()中this,o2就相当于Comparable接口的compareTo()中o
*
*/
public class ComparatorByName implements Comparator {

@Override
public int compare(Object o1, Object o2) {
Student stu1=(Student)o1;
Student stu2=(Student)o2;
int temp=stu1.getName().compareTo(stu2.getName());
return temp==0?stu1.getAge()-stu2.getAge():temp;
}

}


TreeSetDemo2
package com.itheima.collection;

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

import com.itheima.comparator.ComparatorByName;
import com.itheima.domain.Student;

public class TreeSetDemo2 {

/**
* @param args
*/
public static void main(String[] args) {
/*
* 创建TreeSet"集合"对象。
* ComparatorByName对象作为构造函数参数,参与到建立TreeSet“集合”对象过程中
*/
Set set =new TreeSet(new ComparatorByName());
/*
* 添加TreeSet"集合"元素
*/
set.add(new Student("zhangsan",16));
set.add(new Student("lisi",18));
set.add(new Student("zhaoliu",22));
set.add(new Student("wangwu",20));
set.add(new Student("chengxiaoqi",23));
/*
* 获取TreeSet"集合"元素
*/
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
System.out.println(iterator.next());
}
}
}

...全文
160 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
BZHeiZ 2015-10-03
  • 打赏
  • 举报
回复
引用 1 楼 zhuangqingch 的回复:
先跟楼主说下二者的区别吧。 从本质上讲,其实二者并没有区别,都是用于比较对象之间的大小关系。但从比较的操作方式来看,有以下区分: 1、Comparator:名词,比较器。可以理解为一个具体的工具。该工具的作用就是用于比较2个任意对象(当然也可以限制对象类型)的大小关系。由于它是一个接口,所以哪个对象实现了他,就自动变成一种比较工具。拥有了比较任意2个对象大小的功能。 2、Comparable:形容词。可比较的。可以理解是一种特性,主要用于描述某个对象是否拥有能和其他对象比较的特性。它也是一个接口。哪个对象实现了它,则说明该对象具备了与其他对象相互比较的特性。 另外,Comparator提供的比较方法为:compare(比较),而Comparable提供的方法则为compareTo(和谁比较) 所以上述2者可以形象的描述为以下对白: Comparator说:我是一个比较器,我能比较任意2个对象的大小关系 Comparable说:我具备可比较的特性。我可以和其他对象比较大小关系。 简单单调的解释说完了,接下来说下楼主提出的问题: 楼主提出来的问题,实际只要多看下TreeSet的源码,就会明白了。 TreeSet其底层实现,是基于TreeMap实现的。当你new一个TreeSet出来时,底层实际是new了一个TreeMap, TreeSet中的值会转变为TreeMap的key值。而key对象的value值。存储的是一个单例: TreeSet的静态常量:Object对象(private static final Object PRESENT = new Object()),另外TreeSet的大部分操作都是相当于直接操作TreeMap 如每次调用TreeSet的add操作时,实际是执行TreeMap的put(key, value)操作。TreeSet就相当于是TreeMap的keySet。所以想了解TreeSet排序中的比较处理。实际就是了解TreeMap的put操作具体是做了哪些操作。 下面是对楼主疑问的解答: 1、ComparatorByName对象作为构造函数参数是怎么参与到建立TreeSet“集合”对象过程中的。 当执行new TreeSet时,通过构造方法传入。如下代码:

public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
至于比较器如何参与比较过程的,下个问题一起解释。 2、Comparable接口和Comparator接口的比较:o1就相当于Comparable接口的compareTo()中this,o2就相当于Comparable接口的compareTo()中o。 如果只是单独拿这2个接口来说,这样的描述是有问题的。具体原因最前面描述二者区别那已经很明显了。Comparator比较器是比较任意2个对象。方法会接收2个参数。当调用方法时,传入的参数顺序以及方法内部实现没确定前,是无法知道是用A来比较B,还是B来比较A的。而Comparable虽然是“与谁比较”。但比较方法内部的实现没确定前也无法确定是A比较B,还是用B来比较A。 如果是站在TreeSet底层排序比较所使用的角度。需要了解其源码实现,因为TreeSet是基于TreeMap,所以直接了解TreeMap就可以了。在TreeMap中,定义了一个Entry类来表示key-value键值对。并每次添加新的key-value时,会进行排序操作。(实际只对entry.key做排序处理,通过有序的key取值)。Entry类采用了双键表结构来处理排序。其排序的实现操作,是根据key值的大小(即TreeSet中存入的对象),来比较并决定各个Entry在键表中的位置。即每次添加操作。都会为加入的数据在双向键表中寻找对应顺序的位置。下面是TreeMap的put操作的源码实现:

public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        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<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
从源码中可以看出。TreeMap中如果存在比较器Comparator(new TreeMap时传入)。则采用比较器来比较key对象大小,否则将对象强转为Comparable类型进行比较。(该操作可看出,如果TreeMap中没有设置比较器,且存储的key没有实现Comparable接口,在运行阶段将出出现java.lang.ClassCastException异常),对于是用A比较B,还是B比较A,具体根据比较器,或实现Comparable接口的类来决定。最终通过2者的对象比较。新加入的Entry就可以在键表中找到对应位置。最终一个有序的双向键表产生后。如果要获取排序后的key,只需要通过第1个Entry逐个一直next操作就可以了。 这个操作实际就是TreeMap的Iterator迭代时所做的操作,如下代码:

final class KeyIterator extends PrivateEntryIterator<K> {
        KeyIterator(Entry<K,V> first) {
            super(first);
        }
        public K next() {
            return nextEntry().key;
        }
    }
TreeSet或TreeMap的底层实现细节这里就不多说了。楼主可以多看看它们的源码。总体说得有点乱,楼主可以看下有没有疑问,有疑问可以追问。
没有看明白的几点(“”是原文摘要): ·“而key对象的value值。存储的是一个单例: TreeSet的静态常量:Object对象(private static final Object PRESENT = new Object())” 被“卡壳”在如下关键词上:“单例”“静态常量”。你使用这些关键词,想说的话,自己还是无法捕捉到。 ·“在TreeMap中,定义了一个Entry类来表示key-value键值对” 阅读到这时,想插入“关于Map.Entry的一些想法”。 关于Map.Entry的一些想法: Map.Entry,API的解释,是一种映射关系。我看了,还是一头雾水,有一种说不出来的不舒适感,像在“我”和它之间有一块绸布隔着。用上“关系”这一个词,本身就特别含糊其辞,我一直想问,那这关系又是什么呢? ·“Entry<K,V> t = root;” put(K key, V value)源代码,看到第一句,就“卡壳”了!root是一个变量?怎么没见定义?说到这,又想暂时岔开一下主题,插入“关于源代码的一些想法” 关于源代码的一些想法: 一直在看黑马毕向东java教学视频,一直都有听到“底层怎么样,源代码怎么样......”,但也仅仅停留听到,轻描淡写地听到,从没有教我们怎么“看”源代码。自己就也一直非常好奇源代码究竟是长什么样!当我看到源代码时,整个人都呆掉了,天书般难懂!此时此刻,明明知道面前这是一行一行有意义的字母排列组合,但我却无法接受要传递的信息。 再一个想说的是:因为看不懂源代码,所以特别排斥去看它。突然有一个想法,有没有这样的东西,像古文翻译一样的,在每一句源代码下面都有“翻译”,可以对照阅读源代码!
zhuangqingch 2015-09-22
  • 打赏
  • 举报
回复
先跟楼主说下二者的区别吧。 从本质上讲,其实二者并没有区别,都是用于比较对象之间的大小关系。但从比较的操作方式来看,有以下区分: 1、Comparator:名词,比较器。可以理解为一个具体的工具。该工具的作用就是用于比较2个任意对象(当然也可以限制对象类型)的大小关系。由于它是一个接口,所以哪个对象实现了他,就自动变成一种比较工具。拥有了比较任意2个对象大小的功能。 2、Comparable:形容词。可比较的。可以理解是一种特性,主要用于描述某个对象是否拥有能和其他对象比较的特性。它也是一个接口。哪个对象实现了它,则说明该对象具备了与其他对象相互比较的特性。 另外,Comparator提供的比较方法为:compare(比较),而Comparable提供的方法则为compareTo(和谁比较) 所以上述2者可以形象的描述为以下对白: Comparator说:我是一个比较器,我能比较任意2个对象的大小关系 Comparable说:我具备可比较的特性。我可以和其他对象比较大小关系。 简单单调的解释说完了,接下来说下楼主提出的问题: 楼主提出来的问题,实际只要多看下TreeSet的源码,就会明白了。 TreeSet其底层实现,是基于TreeMap实现的。当你new一个TreeSet出来时,底层实际是new了一个TreeMap, TreeSet中的值会转变为TreeMap的key值。而key对象的value值。存储的是一个单例: TreeSet的静态常量:Object对象(private static final Object PRESENT = new Object()),另外TreeSet的大部分操作都是相当于直接操作TreeMap 如每次调用TreeSet的add操作时,实际是执行TreeMap的put(key, value)操作。TreeSet就相当于是TreeMap的keySet。所以想了解TreeSet排序中的比较处理。实际就是了解TreeMap的put操作具体是做了哪些操作。 下面是对楼主疑问的解答: 1、ComparatorByName对象作为构造函数参数是怎么参与到建立TreeSet“集合”对象过程中的。 当执行new TreeSet时,通过构造方法传入。如下代码:

public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
至于比较器如何参与比较过程的,下个问题一起解释。 2、Comparable接口和Comparator接口的比较:o1就相当于Comparable接口的compareTo()中this,o2就相当于Comparable接口的compareTo()中o。 如果只是单独拿这2个接口来说,这样的描述是有问题的。具体原因最前面描述二者区别那已经很明显了。Comparator比较器是比较任意2个对象。方法会接收2个参数。当调用方法时,传入的参数顺序以及方法内部实现没确定前,是无法知道是用A来比较B,还是B来比较A的。而Comparable虽然是“与谁比较”。但比较方法内部的实现没确定前也无法确定是A比较B,还是用B来比较A。 如果是站在TreeSet底层排序比较所使用的角度。需要了解其源码实现,因为TreeSet是基于TreeMap,所以直接了解TreeMap就可以了。在TreeMap中,定义了一个Entry类来表示key-value键值对。并每次添加新的key-value时,会进行排序操作。(实际只对entry.key做排序处理,通过有序的key取值)。Entry类采用了双键表结构来处理排序。其排序的实现操作,是根据key值的大小(即TreeSet中存入的对象),来比较并决定各个Entry在键表中的位置。即每次添加操作。都会为加入的数据在双向键表中寻找对应顺序的位置。下面是TreeMap的put操作的源码实现:

public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        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<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
从源码中可以看出。TreeMap中如果存在比较器Comparator(new TreeMap时传入)。则采用比较器来比较key对象大小,否则将对象强转为Comparable类型进行比较。(该操作可看出,如果TreeMap中没有设置比较器,且存储的key没有实现Comparable接口,在运行阶段将出出现java.lang.ClassCastException异常),对于是用A比较B,还是B比较A,具体根据比较器,或实现Comparable接口的类来决定。最终通过2者的对象比较。新加入的Entry就可以在键表中找到对应位置。最终一个有序的双向键表产生后。如果要获取排序后的key,只需要通过第1个Entry逐个一直next操作就可以了。 这个操作实际就是TreeMap的Iterator迭代时所做的操作,如下代码:

final class KeyIterator extends PrivateEntryIterator<K> {
        KeyIterator(Entry<K,V> first) {
            super(first);
        }
        public K next() {
            return nextEntry().key;
        }
    }
TreeSet或TreeMap的底层实现细节这里就不多说了。楼主可以多看看它们的源码。总体说得有点乱,楼主可以看下有没有疑问,有疑问可以追问。

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧