不明白Arrays.copyOf的源码

Do not understand the source code of Arrays.copyOf

我无法理解 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. 正在检查 newType 是否为对象数组:

    Object[] a1 = new Object[100]; -- array of Objects
    
    String[] a2 = new String[100]; -- array of Strings
    

为什么要这样做?因为 new Object[n] 比 Array.newInstance

  1. Array.newInstance(Class<?> componentType, int... dimensions) 创建一个由第一个参数定义的类型数组,例如 String.class -> String[]。注意 String[].class.getComponentType() returns String.class

  2. 不能这样用,但是可以这样用

    Integer[] nums = Arrays.copyOf(new Object[]{1, 2}, 2, Integer[].class);
    

在这种情况下,它仅取决于元素的实际类型,例如

  Arrays.copyOf(new Object[]{1L, 2}, 2, Integer[].class);

会失败,除了Integer

你不能在Integer[]中写任何东西
  1. what is this line checking?
(Object)newType == (Object)Object[].class

它正在检查变量 newType 是否持有对表示类型 Object[]java.lang.Class 实例的引用。不需要强制转换。

  1. What are the differences between (T[]) new Object[newLength] and (T[]) Array.newInstance(newType.getComponentType(), newLength). why Array.newInstance not good enough for both cases?

据我所知,Array.newInstance() 可以用于这两种情况,但非反射普通数组构造可能会更快一些。因此,我假设 Object[] 出于性能原因被称为特例,但我不知道这种情况是否足够频繁地被执行以使得优化变得重要。

  1. This following line compiles, but crashes at run time (as expected). When should I use this method?
Integer[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Integer[].class) 

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

让我尝试回答这个问题:

要回答您的第一个问题,它会检查 newType 类型是否与数组中的类型相同。两者也都将类型向上转换为 Object 类型。也就是说,它会尝试查看数组的父类型是否为对象。 See this SO question on Upcasting and Downcasting. My guess is that it casts not because to check for type-safety. Even though all objects in Java derive from objects as a superclass.

注意到

会很有帮助
 T[] copy = ((Object)newType == (Object)Object[].class)
    ? (T[]) new Object[newLength]
    : (T[]) Array.newInstance(newType.getComponentType(), newLength);

实际上是一行。即它实际上是一个 if-else 条件。

result = (condition) ? (doThisIfTrue) : (elseDoThisIfFalse)

Simple example here

所以基本上该行与以下内容相同:

T[] copy;
Boolean condition = ((Object)newType == (Object)Object[].class)
if(condition) 
     copy = (T[]) new Object[newLength];
else 
     copy = (T[]) Array.newInstance(newType.getComponentType(), newLength);

在 Array.newInstance 中创建新实例的原因可能是性能选择,如果程序总是必须创建新实例,它将比直接初始化通用对象数组和复制更昂贵事情结束了

Arrays.copyOf 将创建一个新数组(引用旧数组)但具有较新的长度并用空对象填充未使用的位置。 This is what it does on an array of ints 其中用零填充未使用的索引。

Arrays.CopyOf 用于提供对象的浅拷贝,即它引用旧项目,但在 新数组 中。 This SO question has more info on it.

首先是那一行的演员表

((Object)newType == (Object)Object[].class)

绝对需要。删除它们将导致编译错误:

incomparable types: Class<CAP#1> and Class<Object[]>
 where CAP#1 is a fresh type-variable:
  CAP#1 extends T[] from capture of ? extends T[]

现在回答你的问题这一行检查的是什么?

它只是验证给定的数组是否为对象类型,这是您另一个问题的部分答案为什么 Array.newInstance 对这两种情况都不够好?

在第一种情况下,我们已经知道数组是 Object 类型,所以调用 newInstance 方法来检索正确的类型是没有意义的,这只会导致性能损失.

至于你最后的例子,

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

它确实编译,这是真的。因为方法的给定参数都是有效的。它肯定会在运行时失败;将 "a" 转换为 Integer 类型的预期输出是什么?

现在,什么时候使用 copyOf?当您已经知道这两种类型,并且已经知道它们一起有效时。

它的主要用途是 return 原始数组的副本,但用 [null/default 值截断或填充]。

What is this line checking? (Object)newType == (Object)Object[].class

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

异常转换是必要的,因为Class<Object[]>Object[].class的类型)和Class<? extends T[]>是不可比较的类型。基本上,要编译与 == 的相等比较,其中一侧必须是另一侧的子类型或超类型。

即我们做不到:

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

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

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

另一种比较方法是:

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

What are the differences between (T[]) new Object[newLength] and (T[]) Array.newInstance(newType.getComponentType(), newLength)?

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

Why Array.newInstance not good enough for both cases?

使用反射的操作比非反射的操作generally slower

reflection tutorial 说:

Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.

Java SE 充满了这样的微优化。 SE 的作者试图从中榨取一切。

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

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

When should I use this method?

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

  • 加宽:

    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)比较典型。