【问题标题】:Why does Java ArrayList use per-element casting instead of per-array casting?为什么 Java ArrayList 使用按元素强制转换而不是按数组强制转换?
【发布时间】:2012-09-04 03:59:46
【问题描述】:

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

我想知道为什么要这样做。对我来说,他们似乎只是在做不必要的演员表。只创建一个T[] array = (T[]) new Object[n]; 然后只创建一个return array[i]; 而不进行强制转换不是更合乎逻辑并且稍微快一点吗?每个数组创建只有一次强制转换,通常小于读取次数。

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

【问题讨论】:

标签: java casting arraylist


【解决方案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 语言的设计者要求我们明确使用哪种组件类型。

【讨论】:

  • 嗯...有趣。但是,如果两者都不会产生运行时开销,那为什么还要对“通用数组创建”大惊小怪呢?为什么编译器不能将T[] array = new T[n] 替换为T[] array = (T[]) new Object[n] 作为语法糖服务?
【解决方案2】:

每当从中读取一个元素时,都会进行一次转换return (T) array[i];。因此,每次阅读都要进行演员表。

Generic 是一种编译时检查。在运行时使用类型 T extends 代替。在这种情况下 T 隐含 extends 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);

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

【讨论】:

    【解决方案3】:

    我认为更多的是代码风格而不是性能或类型安全(因为支持数组是私有的)

    java 5 ArrayList 是按照您建议的E[] 数组实现的。如果您查看源代码,您会发现它包含 7 个 (E[]) 类型转换。从 java 6 开始,ArrayList 更改为使用 Object[] 数组,这导致只有 3 (E) 个强制转换。

    【讨论】:

      【解决方案4】:

      数组也是对象。 这里T[] array = (T[]) new Object[n] 你只转换 (T[]) 对象类型而不是数组中的元素。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-28
        • 2020-07-02
        • 2012-02-10
        • 2016-08-05
        • 2011-04-24
        • 2011-06-11
        相关资源
        最近更新 更多