使用TreeSet出現cannot be cast to java.lang.Comparable
阿新 • • 發佈:2022-03-29
事情起源於一次使用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<? extendsE>) 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);
}
}