不明白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) 

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

这是检查简单的平等(可能是为了微观优化的目的,但稍后更多)。

因为Class<Object[]>Object[].class )和Class<? extends T[]> Class<? extends T[]>是无与伦比的types。 基本上,为了与==进行等式比较来进行编译,其中一方必须是另一方的子types或超types。

即我们不能这样做:

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

genericstypes的规则有点复杂,有些情况下比较不能编译,但是仍然可以评估为真。

Class<Object[]>不是Class<? extends T[]>的超types的原因 尽pipeObject[]是所有对象数组types的超types,但Class<? extends T[]>是因为没有通配符, Javagenerics是不变的。

另一种做比较的方法是:

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

(T[]) new Object[newLength](T[]) Array.newInstance(newType.getComponentType(), newLength)什么(T[]) Array.newInstance(newType.getComponentType(), newLength)

  • new Object[...]创build一个静态已知types的常规方法。 请记住,代码刚刚检查过T[]Object[]
  • Array.newInstance(...)使用reflection来dynamic创build一个传入的Classtypes的数组。

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

使用reflection的操作通常比其非reflection对应物慢 。

反思教程说:

由于reflection涉及dynamicparsing的types,因此无法执行某些Java虚拟机优化。 因此,reflection操作的性能要比非reflection操作的性能要差,应该在对性能敏感的应用程序中频繁调用的代码段中避免。

Java SE充满了这样的微型优化。 SE的作者试图挤出所有可能的东西。

但在这种情况下,我不会担心性能问题: newInstancecopyOf是HotSpot内在函数 。 这意味着理想情况下这些方法的调用将被机器特定的组件所取代。 有趣的是,我运行了一些testing,发现new Object[...]Array.newInstance(...)之间的差异可以忽略不计。 问题中的代码可能是遗留的,尽pipe它可能在装备不足的JVM上仍然有用。

也可以通过严格的安全性(如小应用程序)在某些情况下禁用reflection,但通常不能用于正常的桌面应用程序。

我应该什么时候使用这种方法?

一般来说,你可能永远不会使用这个过载。 这个重载只有在你想改变数组的types时才有用。

  • 拓宽:

     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)更为典型。

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

它检查variablesnewType是否保存对表示Object[]types的java.lang.Class实例的引用。 演员是不需要的。

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

据我所知,在这两种情况下可以使用Array.newInstance() ,但是非reflection的普通数组构造可能会快一点。 因此,我认为由于性能的原因, Object[]被称为特例,但我不知道这种情况是否被频繁执行,以使优化变得重要。

  1. 下面这行代码编译,但在运行时崩溃(如预期的那样)。 我应该什么时候使用这种方法?
 Integer[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Integer[].class) 

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

  1. 它检查newType是否是Object的数组:

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

为什么这样做? 因为新的Object [n]比Array.newInstance快

  1. Array.newInstance(Class<?> componentType, int... dimensions)创build由第一个参数定义的types数组,例如String.class – > String[] 。 请注意, String[].class.getComponentType()返回String.class

  2. 你不能像这样使用它,但它可以是这样的

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

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

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

会失败,你不能在Integer[]写入Integer

首先,在这一行中投

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

是absobuletly需要的。 删除它们将导致编译错误:

 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[] 

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

它只是validation给定的数组是否是对象types,这是你的其他问题的答案的一部分为什么Array.newInstance不够好的两种情况?

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

至于你最后的例子,

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

它确实编译,这是真的。 因为这个方法的参数都是valids。 它肯定会在运行时失败; 将“a”转换为Integertypes的预期输出是什么?

现在,什么时候使用copyOf ? 当你已经知道这两种types,并已知道他们在一起有效。

它的主要用法是返回一个副本,但截断或用[null /默认值]填充到原始数组。

让我试着回答这个问题:

为了回答你的第一个问题,它检查newTypetypes是否与数组中的types相同。 两者都将types转换为对象types。 也就是说,它试图查看数组的父types是否是对象。 在上传和下载中看到这个问题 。 我的猜测是,这不是因为检查types安全性。 尽pipeJava中的所有对象都是作为超类派生的。

注意到这将是有帮助的

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

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

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

简单的例子

所以基本上这条线将是相同的:

 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中创build一个新实例的原因可能是性能的select,如果程序总是需要创build新的实例,那么比直接初始化一个通用对象数组和复制东西要花费更多。

Arrays.copyOf将创build一个新的数组(引用旧数组),但是具有更新的长度,并将未使用的位置用空对象填充。 这就是它在一个int数组上进行的操作,它用零填充未使用的索引。

Arrays.CopyOf用于提供对象的浅拷贝,即它指的是旧项目,但在一个新的数组中 。 这个问题有更多的信息。