【问题标题】:Java List toArray(T[] a) implementationJava List toArray(T[] a) 实现
【发布时间】:2011-09-26 09:37:22
【问题描述】:

我只是在看List接口中定义的方法:

以正确的顺序返回一个包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。如果列表适合指定的数组,则在其中返回。否则,将使用指定数组的运行时类型和此列表的大小分配一个新数组。 如果列表适合指定的数组并有剩余空间(即,数组的元素多于列表),则数组中紧跟集合末尾的元素设置为 null。仅当调用者知道列表不包含任何空元素时,这对确定列表的长度很有用。

<T> T[] toArray(T[] a);

我只是想知道为什么它是这样实现的,基本上如果你将一个长度为

此外,如果您使用列表的大小向它传递一个足够长的数组,如果返回与对象相同的对象 - 实际上返回它没有意义,因为它是相同的对象,但为了清楚起见可以。

问题是我认为这会导致代码效率低下,我认为 toArray 应该简单地接收类并返回带有内容的新数组。

有什么理由不这样编码吗?

【问题讨论】:

  • 您认为效率低下到底是什么?
  • 如果你传递一个大小为
  • 请注意,对于大多数用途,您可以拥有private static final T[] = new T[0],如果数组大小未知,则使用它,以最小化开销。

标签: java list generics toarray


【解决方案1】:

我的猜测是,如果您在调用toArray(T[]) 时已经知道T 的具体类型,那么只声明一个数组比调用List 实现调用@ 更高效987654324@ 给你——而且在很多情况下你可以重复使用这个数组。

但如果它让你感到厌烦,写一个实用方法很容易:

public static <E> E[] ToArray(Collection<E> c, Class<E> componentType) {
    E[] array = (E[]) Array.newInstance(componentType, c.size());
    return c.toArray(array);
}

(请注意,无法编写&lt;E&gt; E[] ToArray(Collection&lt;E&gt; c),因为没有Class 对象就无法在运行时创建E 数组,也无法为E 获取Class 对象在运行时,因为泛型已被删除。)

【讨论】:

    【解决方案2】:

    此方法是 Java 1.5 之前的保留方法。这是javadoc的链接

    在当时,这是将列表转换为可具体化数组的唯一方法。

    这是一个不为人知的事实,但尽管您可以在 Object[] 数组中存储任何内容,但您不能将此数组转换为更具体的类型,例如

    Object[] generic_array = { "string" };
    
    String[] strings_array = generic_array; // ClassCastException
    

    看起来更高效的List.toArray() 就是这样做的,它创建了一个通用的Object 数组。

    在 Java 泛型之前,进行类型安全传输的唯一方法是拥有这样的组合:

    String[] stronglyTypedArrayFromList ( List strings )
    {
        return (String[]) strings.toArray( new String[] );
        // or another variant
        // return (String[]) strings.toArray( new String[ strings.size( ) ] );
    }
    

    谢天谢地,泛型让这种诡计过时了。保留此方法是为了向后兼容 1.5 之前的代码。

    【讨论】:

      【解决方案3】:

      正如其他人所提到的,有几个不同的原因:

      • 您需要以某种方式传入类型,传入指定类型的数组并不是不合理的做法。诚然,如果有一个版本可以传递给您想要的类型的 Class,这可能会很好,以提高速度。
      • 如果你想重复使用你的数组,你可以一直传入同一个数组,而不是每次都需要创建一个新数组。这可以节省时间和内存,如果您需要多次执行 GC 问题

      【讨论】:

      • 我想唯一的好处是你可以重用数组,但我仍然认为应该有一个类参数的重载。
      【解决方案4】:
        357       public <T> T[] toArray(T[] a) {
        358           if (a.length < size)
        359               // Make a new array of a's runtime type, but my contents:
        360               return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        361           System.arraycopy(elementData, 0, a, 0, size);
        362           if (a.length > size)
        363               a[size] = null;
        364           return a;
        365       }
      

      也许它有一个运行时类型?

      来自维基:

      因此,实例化一个 Java 参数化类型的类是 不可能,因为实例化 需要调用构造函数, 如果类型为 未知。

      【讨论】:

      • 仍然没有理由他们不只是传递一个类参数并使用它而不是 a.getClass()。但是,如果有人传递一个数组
      • 为什么不把类参数传给它,只返回数组呢?
      • @Voo 实际上在建议该方法的 List 抽象类中,它甚至建议在 cmets 上,为您传递一个大小为 0 的数组,这更奇怪。
      【解决方案5】:

      这很可能是为了让您可以重用数组,因此您基本上可以避免某些用例的(相对昂贵的)数组分配。另一个小得多的好处是调用者可以稍微更有效地实例化数组,因为 toArray() 必须使用 'java.lang.reflect.Array.newInstance' 方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-12
        • 1970-01-01
        • 2015-04-08
        • 2011-10-09
        • 2022-06-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多