Java 20 API docs的标准库中的包java.util中的Arrays.sort(T[] a, Comparator<? super T> c)的javadoc代码如下:

公共静态空排序(T[]a,比较器?超级T&GT;c) 根据指定比较器产生的顺序对指定的对象数组进行排序.All elements in the array must be mutually comparable by the specified comparator (that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the array).

这种排序保证是稳定的:相等的元素不会因为这种排序而被重新排序.

实施说明:该实现是一种稳定的、自适应的迭代合并排序,当输入数组被部分排序时,所需的比较数远少于n个LG(N),而当输入数组被随机排序时,它提供了传统合并排序的性能.如果输入数组几乎已排序,则实现需要大约n个比较.临时存储要求从接近排序的输入数组的小常量到随机排序的输入数组的n/2对象引用不等.

该实现在其输入数组中平等地利用升序和降序,并且可以利用同一输入数组的不同部分的升序和降序.它非常适合合并两个或更多已排序的数组:只需连接数组并对结果数组进行排序.

该实现改编自Tim Peters的List Sort for Python(TimSort).它使用了Peter McIlroy在《第四届ACM-SIAM离散算法研讨会论文集》,第467-474页,1993年1月发表的《乐观排序和信息理论复杂性》中的技术.

类型参数: T-要排序的对象的类 参数: A-要排序的数组 C-用于确定数组顺序的比较器.空值表示应使用元素的自然排序. 投掷: ClassCastException - if the array contains elements that are not mutually comparable using the specified comparator个 IlLegalArgumentException-(可选),如果发现比较器违反了比较器约定

重点是我的.

编译器不是保证(通过使用类型系统和方法的泛型签名)a的所有元素使用c实际上是相互比较的,并且如果c不为空,c.compare(e1, e2)永远不会抛出ClassCastException吗?

假定,删除类型参数后的"实际"签名toArray将如下所示:static void sort(Object[] a, Comparator c),而c.compare的签名(假设c在我们的程序中具有静态类型Number)将如下所示:int compare(Object o1, Object o2),并且在其实现中将包含两个到Number的强制转换:

Number e1 = (Number) o1;
Number e2 = (Number) o2;
//...

当且仅当运行时类型o1o2Number或子类型Number时,这些强制转换才会成功.

当且仅当a的运行时类型是Number[]或子类型Number[](例如Integer[])时,o1o2的运行时类型是Number或子类型Number.

当且仅当a的静态类型是Number[]或子类型Number[]时,a的运行时类型是Number[]或子类型Number[].(如果a的运行时类型是not Number[]或子类型Number[],但静态类型a是,则将抛出ClassCastException before输入Arrays.sort的正文)

静态类型a要么是Number[],要么是Number[]的子类型,这源于方法签名static <T> void sort(T[] a, Comparator<? super T> c),因为c的静态类型是Number(? super TNumber,因此T要么是Number,要么是Number的子类型).

我写了一些测试,试图让Arrays.sort抛出ClassCastException,只有在c通过null而不是真正的比较器时才真正得到它.

package org.example.basics.generics.arrays;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Comparator;

class ArraysTest {

    private static final Comparator<Number> compareAsWholeNumber = new Comparator<Number>() {
        @Override
        public int compare(java.lang.Number o1, java.lang.Number o2) {
            return Long.compare(o1.longValue(), o2.longValue());
        }
    };

    @Test
    public void sortingIntsThrowsNoException() {
        Integer[] ints = new Integer[]{4, 2, 3};
        Assertions.assertDoesNotThrow(() -> {
            Arrays.sort(ints, compareAsWholeNumber);
        });

    }

    @Test
    public void sortingDoublesThrowsNoException() {
        Double[] doubles = new Double[]{4.0, 2.0, 3.0};
        Assertions.assertDoesNotThrow(() -> {
            Arrays.sort(doubles, compareAsWholeNumber);
        });
    }

    @Test
    public void sortingNumbersThrowsNoException() {
        Number[] numbers = new Number[]{4.0, 2, 3.0f};
        Assertions.assertDoesNotThrow(() -> {
            Arrays.sort(numbers, compareAsWholeNumber);
        });
    }

//    @Test
//    public void sortingObjectsIsCompileError() {
//        Object[] numbers = new Number[]{4.0, 2, 3.0f};
//        Arrays.sort(numbers, compareAsWholeNumber);
//    }

    @Test
    public void sortingDisguisedStringsThrowsClassCastException() {
        Throwable e = Assertions.assertThrows(ClassCastException.class, () -> {
            Arrays.sort((Number[]) new Object[]{"4.0", "2", "3.0f"}, compareAsWholeNumber);
        });
        e.printStackTrace();
    }

    @Test
    public void sortingDisguisedStringsThrowsClassCastExceptionAtArrayAssignment() {
        Assertions.assertThrows(ClassCastException.class, () -> {
            Number[] numbers = (Number[]) new Object[]{"4.0", "2", "3.0f"};
        });
    }
    @Test
    public void sortingNonComparableWithNullComparatorThrowsClassCastException() {
        Number[] numbers = new Number[]{4.0, 2, 3.0f};
        Throwable e = Assertions.assertThrows(ClassCastException.class, () -> {
            Arrays.sort(numbers, null);
        });
        e.printStackTrace();
    }
}

上面的测试套件在我的机器上使用的是Eclipse Temurin 20.0.2成功运行.

特别要注意最后4次测试.

sortingObjectsIsCompileError:传递静态类型既不是Number[]也不是Number[]sort的子类型的数组是编译错误. 取消对该测试的注释并try 编译将为我生成以下结果:

TestToArray.java:46:错误:找不到适合排序的方法(Object[],Compare ator) Arrays.sorte(Numbers,CompareAsWholeNumber); ^ 方法array.&lt;T#1&>排序(T#1[],比较器&lt;?Super T#1&>不适用 (推理变量T#1具有不兼容的边界 上限:数字、对象 下限:对象) 方法array.&lt;T#2&>排序(T#2[],int,int,比较器&lt;?Super T#2&>不适用 (无法推断类型变量(S)T#2 (实际参数列表和正式参数列表的长度不同) 其中T#1、T#2是类型变量: T#1扩展方法&lt;T#1&>Sort(T#1[],比较器&lt;?)中声明的对象超级T#1&GT;) T#2扩展方法&lt;T#2&gt;Sort(T#2[],int,int,Compare&lt;?)中声明的对象.超级T#2&GT;)

sortingDisguisedStringsThrowsClassCastException:顾名思义,这里确实抛出了ClassCastException,但这具有误导性:将堆栈跟踪输出到std_out会产生以下结果(为简洁起见进行了简化):

Java.lang.ClassCastException:类[Ljava.lang.Object;不能强制转换为类[Ljava.lang.Number;([Ljava.lang.Object;和[Ljava.lang.Number;都在加载器‘bootstrap’的模块java.base中]) 在org.example.basics.generics.arrays.TestToArray.lambda$sortingDisguisedStringsThrowsClassCastException$3(TestToArray.java:52)&lt;3条内部线路&gt; 在org.example.basics.generics.arrays.TestToArray.sortingDisguisedStringsThrowsClassCastException(TestToArray.java:51) AT java.base/java.util.ArrayList.forEach(ArrayList.java:1511)&lt;9条内部线路&gt; 在java.base/java.util.ArrayList.forEach(ArrayList.java:1511)&lt;30条内部线路&gt; 在jdk.proxy1/jdk.proxy1.$Proxy2.top(来源不详)&lt;7条内部线路&gt; 在worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) 在worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

请注意:1.失败的强制转换是从[Ljava.lang.Object[Ljava.lang.Number,而不是我们预期的从java.lang.Objectjava.lang.Number,因为它是针对compare方法中的失败强制转换的;2.ClassCastException是从TestToArray类内部抛出的,not是从Arrays类抛出的.

测试sortingDisguisedStringsThrowsClassCastExceptionAtArrayAssignment显示,简单地将String[]的数组赋给Number[]的数组会抛出相同的ClassCastException.

最后一个测试sortingNonComparableWithNullComparatorThrowsClassCastException实际上抛出了预期的ClassCastException,这是我唯一能够让sort方法本身抛出此异常的情况.将堆栈跟踪打印到STD_OUT会产生以下结果(为简洁起见进行了简化):

Java.lang.ClassCastException:类java.lang.Double不能强制转换为类java.lang.Integer(java.lang.Double和java.lang.Integer在加载器‘bootstrap’的模块java.base中) 在java.base/java.lang.Integer.compareTo(Integer.java:72) 在java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320) 在java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188) 在java.base/java.util.Arrays.sort(Arrays.java:1041) 在java.base/java.util.Arrays.sort(Arrays.java:1228) 在org.example.basics.generics.arrays.TestToArray.lambda$sortingNonComparableWithNullComparatorThrowsClassCastException$5(TestToArray.java:67)&lt;30条内部线路&gt; 在jdk.proxy1/jdk.proxy1.$Proxy2.top(来源不详)&lt;7条内部线路&gt; 在worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) 在worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

注意,在本例中,失败的强制转换是从java.lang.Double到java.lang.Integer,而异常实际上是从Arrays类抛出的.

Arrays.sort(T[] a, Comparator<? super T> c)可以抛出ClassCastException的唯一情况是为比较器c传递值null的情况吗?

背景:我希望找出我需要犯什么样的错误,才能使类型系统不再起到ClassCastExceptionsort的保护作用.到目前为止,编译器似乎至少会发出一个"未判断的"警告,这是令人放心的.有没有办法从sort得到ClassCastException,而不需要编译器发出"未经判断的"警告(并且不会用null代替c)?

推荐答案

如果您有原始类型(无论是意外的还是非意外的),则触发该异常非常简单:

class FawltyComparators {
    @Test
    void test() {
        final Comparator numberComparer = new NumberComparator();
        final Object[] array = { 1, 2, "3", "4" };
        Arrays.sort(array, numberComparer);
    }

    private static final class NumberComparator implements Comparator<Number> {
        @Override
        public int compare(final Number o1, final Number o2) {
            return 0; // doesn't matter
        }
    }
}

但这仍然会给您编译器警告:

FawltyCompators.java:17: warning: [unchecked] unchecked method invocation: method sort in class Arrays is applied to given types
        Arrays.sort(array, numberComparer);
                   ^
  required: T[],Comparator<? super T>
  found:    Object[],Comparator
  where T is a type-variable:
    T extends Object declared in method <T>sort(T[],Comparator<? super T>)
FawltyCompators.java:17: warning: [unchecked] unchecked conversion
        Arrays.sort(array, numberComparer);
                           ^
  required: Comparator<? super T>
  found:    Comparator
  where T is a type-variable:
    T extends Object declared in method <T>sort(T[],Comparator<? super T>)

Java相关问答推荐

收听RDX中用户数据的变化

Cosmos Change Feed Process Lag远远超过收集中的记录数量

空手道比赛条件

Java .类参数不通过构造函数传递

如何使用AWS CLI从S3存储桶中的所有对象中删除用户定义的元数据?

Java LocalTime.parse在本地PC上的Spring Boot中工作,但在Docker容器中不工作

在AVL树的Remove方法中使用NoSuchElementException时遇到问题

Java编译器抛出可能未正确初始化的错误?

如何正确创建序列图?

如何对多个字段进行分组和排序?

格式中的特定回录键-值对

在具有Quarkus Panache的PostgreSQL中将JSON数据存储为JSONB时,会将其存储为转义字符串

H2数据库仅支持%1个结果集?

多线程、并发和睡眠未按预期工作

MapStruct记录到记录的映射不起作用

message.acknowledge()没有';在使用Spring Boot在ActiveMQ中读取消息后,t将消息出列

为什么Java编译器为没有参数的方法(getter方法)创建桥接方法

java.util.LinkedList()是如何成为MutableList的实例的?

UuidGenerator Bean 类型不匹配?

在对象列表上调用提取后,如何判断没有值为空?