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

 

posted @ 2022-03-29 22:51  听雷雨声  阅读(506)  评论(0)    收藏  举报