62,615
社区成员
发帖
与我相关
我的任务
分享
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;
}
}
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());
}
}
}
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的底层实现细节这里就不多说了。楼主可以多看看它们的源码。总体说得有点乱,楼主可以看下有没有疑问,有疑问可以追问。