1. 程式人生 > 其它 >使用TreeSet出現cannot be cast to java.lang.Comparable

使用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);
}
}