不理解Arrays.copyOf的源代码

2022-09-01 01:06:39

我无法理解 的源代码。Arrays.copyOf

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}
  1. 这行检查是什么?

    (Object)newType == (Object)Object[].class
    
  2. 和 之间有什么区别。为什么两种情况都不够好?(T[]) new Object[newLength](T[]) Array.newInstance(newType.getComponentType(), newLength)Array.newInstance

  3. 以下行将进行编译,但在运行时崩溃(如预期的那样)。何时应使用此方法?

    Integer[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Integer[].class) 
    

答案 1

什么是线路检查?(Object)newType == (Object)Object[].class

它正在检查简单的相等性(可能用于微优化的目的,但稍后会详细介绍)。

不寻常的铸造是必要的,因为(的类型)并且是不可比拟的类型。基本上,为了与编译进行相等比较,其中一方必须是另一方的子类型或超类型。Class<Object[]>Object[].classClass<? extends T[]>==

也就是说,我们不能做:

// doesn't compile
// this expression can never evaluate to true
(new Integer(0) == new Float(0f))

泛型类型的规则有点复杂,在某些情况下,比较无法编译,但仍可能计算为 true。

原因不是 的超类型,尽管它是所有对象数组类型的超类型,但 Java 泛型是不变的,不存在通配符。Class<Object[]>Class<? extends T[]>Object[]

另一种比较方法是:

(newType == (Class<? extends Object[]>)Object[].class)

和 之间有什么区别?(T[]) new Object[newLength](T[]) Array.newInstance(newType.getComponentType(), newLength)

  • new Object[...]以正常方式创建静态已知类型的数组。请记住,代码刚刚检查过,即 。T[]Object[]
  • Array.newInstance(...)使用反射来动态创建传入类型的数组。Class

为什么这两种情况都不够好?Array.newInstance

使用反射的操作通常比其非反射操作

反射教程说:

由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。因此,反射操作的性能低于非反射操作,因此在性能敏感型应用程序中经常调用的代码节中应避免使用反射操作。

Java SE充满了这样的微优化。SE的作者试图从中榨取他们所能挤出的一切。

但是在这种情况下,我不会担心性能会受到影响:newInstancecopyOfHotSpot的内部函数。这意味着理想情况下,对这些方法的调用将被替换为特定于计算机的程序集。有趣的是,我进行了一些测试,发现两者之间的差异可以忽略不计。问题中的代码可能是遗物,尽管它在装备较差的JVM上可能仍然有用。new Object[...]Array.newInstance(...)

在某些具有严格安全性的上下文中(如小程序),也可以禁用反射,但通常不适用于普通的桌面应用程序。

何时应使用此方法?

通常,您可能永远不会使用此重载。仅当要更改数组的类型时,此重载才有用。

  • 扩大:

    Object[] a = Arrays.copyOf(
        new String[] { "hello", "world" }, 3, Object[].class);
    a[2] = Character.valueOf('!');
    System.out.println(Arrays.toString(a));
    
  • 缩小:

    String[] a = Arrays.copyOf(
        new Object[] { "hello", "world" }, 2, String[].class);
    System.out.println(String.join(" ", a));
    

使用Arrays.copyOf(T[],int)更为典型。


答案 2
  1. 这行检查是什么?
(Object)newType == (Object)Object[].class

它正在检查变量是否包含对表示类型的实例的引用。不需要演员阵容。newTypejava.lang.ClassObject[]

  1. 和 之间有什么区别。为什么两种情况都不够好?(T[]) new Object[newLength](T[]) Array.newInstance(newType.getComponentType(), newLength)Array.newInstance

据我所知,可以在这两种情况下使用,但非反射式普通阵列构造可能更快一些。因此,出于性能原因,我认为这被作为一个特殊情况,但我不知道这种情况是否足够频繁地执行,以使优化变得重要。Array.newInstance()Object[]

  1. 以下行将进行编译,但在运行时崩溃(如预期的那样)。何时应使用此方法?
Integer[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Integer[].class) 

当您需要将数组复制到可能具有不同(但兼容)元素类型的数组时,尤其是当元素类型在静态上不是已知的时,应使用它。如果您知道您希望副本具有与原始副本相同的元素类型,那么使用原始数组的方法会更容易。clone()