为什么 Java ArrayList 使用每个元素的强制转换而不是每个数组的强制转换?

2022-09-02 04:16:00

在Java(可能还有许多其他类)内部发生的事情是,有一个内部的,对象被写入其中。每当从中读取元素时,就会进行强制转换。所以,每一次阅读都有一个演员。ArrayList<T>Object[] array = new Object[n];Treturn (T) array[i];

我想知道为什么要这样做。在我看来,他们似乎只是在做不必要的演员。只是创建一个,然后不进行强制转换,岂不是更合乎逻辑,也更快一些?每次创建数组只有一次强制转换,这通常远远少于读取次数。T[] array = (T[]) new Object[n];return array[i];

为什么他们的方法更受欢迎?我不明白为什么我的想法不是严格意义上更好?


答案 1

它比这更复杂:泛型在字节码中被擦除,并且擦除是 。同样,的返回值变为 。为了保持类型系统的完整性,在实际使用类时插入一个检查的强制转换,即T[]Object[]get()Object

Integer i = list.get(0);

将被删除到

Integer i = (Integer) list.get(0);

在这种情况下,ArrayList中的任何类型检查都是多余的。但这确实是无关紧要的,因为两者和和都是未经检查的强制转换,并且不会产生运行时开销。(T)(T[])

可以编写一个已检查的 ArrayList,它具有以下功能:

T[] array = Array.newInstance(tClass, n);

这将防止堆污染,但代价是冗余类型检查(您无法在调用代码中抑制合成转换)。它还要求调用方向 ArrayList 提供元素类型的类对象,这会使它的 api 变得混乱,并使其更难在泛型代码中使用。

编辑:为什么禁止创建泛型数组?

一个问题是数组被检查,而泛型是未选中的。那是:

Object[] array = new String[1];
array[0] = 1; // throws ArrayStoreException

ArrayList list = new ArrayList<String>();
list.add(1); // causes heap pollution

因此,数组的组件类型很重要。我认为这就是为什么Java语言的设计者要求我们明确使用哪种组件类型。


答案 2

每当从中读取元素时,就会进行强制转换。所以,每一次阅读都有一个演员。return (T) array[i];

泛型是编译时检查。在运行时,将改用 T 类扩展。在这种情况下,隐式地所以你在运行时拥有的东西是有效的。Textends Object

return (Object) array[i];

return array[i];

创建一个不是更合乎逻辑,也更快一点吗?

T[] array = (T[]) new Object[n]

没有。同样在运行时,这成为

Object[] array = (Object[]) new Object[n];

Object[] array = new Object[n];

你真正钓鱼的是

T[] array = new T[n];

除了这不会编译,主要是因为T在运行时是未知的。

您可以做的是

private final Class<T> tClass; // must be passed in the constructor

T[] array = (T[]) Array.newInstance(tClass, n);

只有这样,数组才会真正成为预期的类型。这可以使读取速度更快,但以写入为代价。主要好处是快速检查失败,即您可以阻止正在损坏的集合,而不是等到检测到它已损坏时才引发异常。


推荐