使用TreeSet出现cannot be cast to java.lang.Comparable
事情起源于一次使用set来去重的需求。一开始使用的HashSet,然而实际运行后发现,每次返回给前端的数据顺序都是变的,虽然结果一样,但是对用户来说这是一个不好的体验,然后我把HashSet换成了TreeSet,于是问题出现了,Java虚拟机报了这样一个错误:java.lang.ClassCastException: com.example.demo.DemoApplicationTests$TestClass cannot be cast to java.lang.Comparable。从错误类型来看这是一个类型转换的问题,为什么会出现这样的问题呢。
在TreeSet的源码中我们可以看到,TreeSet是通过使用HashMap来达到去重的功能的。
public boolean addAll(Collection<? extends E> c) { // Use linear-time version if applicable if (m.size()==0 && c.size() > 0 && c instanceof SortedSet && m instanceof TreeMap) { SortedSet<? extends E> set = (SortedSet<? extends E>) c; TreeMap<E,Object> map = (TreeMap<E, Object>) m; Comparator<?> cc = set.comparator(); Comparator<? super E> mc = map.comparator(); if (cc==mc || (cc != null && cc.equals(mc))) { map.addAllForTreeSet(set, PRESENT); return true; } } return super.addAll(c); }
而在HashMap的源码中,我们可以看到这样一行代码,这就是报错的原因,HashMap首先检查自身是否设置了实现了Comparator接口的类,如果有就使用compare方法进行排序,否者就把尝试把目标类强转为Comparable类。
/** * Compares two keys using the correct comparison method for this TreeMap. */final int compare(Object k1, Object k2) { return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2) : comparator.compare((K)k1, (K)k2); }
从源码可以知道,TreeSet是通过HashMap来实现去重的,而他的有序实际上是通过排序实现的,并非是一种先进先出的有序。回到一开始的问题,我们可以用两种方法解决它,实现Comparator接口或者实现Comparable接口。
void setTest() { List<TestClass> testClasses = new ArrayList<>(); for (int i = 0; i < 5; i++) { TestClass testClass = new TestClass(); testClass.setB(i); testClasses.add(testClass); }
HashSet<TestClass> hashSet = new HashSet<>(testClasses);
// 方法一
MyComparator myComparator = new MyComparator();
TreeSet<TestClass> treeSet1 = new TreeSet<>(myComparator);
treeSet1.addAll(testClasses);
// 方法二
TreeSet<TestClass> treeSet2 = new TreeSet<>(testClasses);
System.out.println(hashSet);
System.out.println(treeSet1);
System.out.println(treeSet2);
}
// 方法一 class MyComparator implements Comparator<TestClass> { @Override public int compare(TestClass o1, TestClass o2) { return Integer.compare(o1.getB() - o2.getB(), 0); } }
// 方法二
class TestClass implements Comparable<TestClass> {
String a = "4564";
Integer b = 46578;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public Integer getB() {
return b;
}
public void setB(Integer b) {
this.b = b;
}
@Override
public int compareTo(TestClass o) {
return Integer.compare(this.getB() - o.getB(), 0);
}
}

浙公网安备 33010602011771号