44

I have trouble understanding the source code of 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. what is this line checking?

    (Object)newType == (Object)Object[].class
    
  2. 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?

  3. 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) 
    
Tiny
  • 27,221
  • 105
  • 339
  • 599
Xin
  • 1,169
  • 1
  • 10
  • 20
  • 2
    Partial answer: `Serializable[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Serializable[].class);` should work and demonstrates what the method can be used for.. – biziclop Apr 07 '15 at 22:22
  • 1
    Because although `String instanceof Serializable`, `String[] is not instanceof Serializable[]` – biziclop Apr 07 '15 at 22:24

5 Answers5

22

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

It's checking simple equality (likely for the purpose of a micro-optimization, but more on that later).

The unusual casting is necessary because Class<Object[]> (the type of Object[].class) and Class<? extends T[]> are incomparable types. Basically, for an equality comparison with == to compile, one of the sides has to be a subtype or supertype of the other.

I.e. we can't do:

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

The rules for generic types are a bit more complicated and there are a few cases where a comparison doesn't compile, but it may still evaluate to true.

The reason Class<Object[]> is not a supertype of Class<? extends T[]>, despite Object[] being a supertype of all object array types, is that Java generics are invariant without the presence of a wildcard.

Another way to do the comparison would be:

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

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

  • new Object[...] creates an array the normal way, of a type that is statically known. Remember, the code has just checked that T[] is Object[].
  • Array.newInstance(...) uses reflection to dynamically create an array of the Class type passed in.

Why Array.newInstance not good enough for both cases?

An operation using reflection is generally slower than its non-reflective counterpart.

The reflection tutorial says:

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 is filled with micro-optimization like this. The writers of SE try to squeeze everything they can out of it.

But I wouldn't be worried about a performance hit in this case: newInstance and copyOf are HotSpot intrinsics. This means that ideally calls to these methods get replaced with machine-specific assembly. Anecdotally, I ran some tests and found the difference between new Object[...] and Array.newInstance(...) to be negligible. The code in the question is probably a relic, although it may still be useful on less well-equipped JVMs.

Reflection can also be disabled in certain contexts with strict security (such as an applet), but not typically for a normal desktop application.

When should I use this method?

In general, you will probably never use this overload. This overload is only useful if you want to change the type of the array.

  • Widening:

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

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

It's more typical to use Arrays.copyOf(T[], int).

Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Is there a reason that makes Object class so special? I guess there are plenty of other classes to check to "try to squeeze everything they can out of it." – Xin Apr 08 '15 at 01:26
  • `Object[]` is very common, especially internal to Collections classes. Also see [this comment](http://stackoverflow.com/questions/29494800/java-do-not-understand-the-source-code-of-arrays-copyof/29502668?noredirect=1#comment47149887_29495113) by @JeffreyBosboom. (`ArrayList` in particular uses it.) *"plenty of other classes to check"* The cost of conditional checks will pretty quickly outweigh the *very* small benefit gained by eliding the reflection. That's the trouble with this type of micro-optimization. – Radiodef Apr 08 '15 at 01:36
14
  1. what is this line checking?
(Object)newType == (Object)Object[].class

It is checking whether variable newType holds a reference to an instance of java.lang.Class representing type Object[]. The casts are unneeded.

  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?

As far as I can tell, Array.newInstance() could be used in both cases, but non-reflective ordinary array construction is likely a bit faster. Thus, I suppose that Object[] is called out as a special case for performance reasons, but I have no idea whether that case is exercised frequently enough for the optimization to be important.

  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) 

You should use it when you need to copy an array to an array with a possibly different (but compatible) element type, especially when the element types are not statically known. If you know you want the copy to have the same element type as the original, then it's easier to use the original array's clone() method.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • e.g., `Number[] nums = Arrays.copyOf(new Integer[]{1,2,2},3,Number[].class);` should work. Also notice that many old library functions retain some old optimizations that are effective in older versions of the JVM. So while they might be confusing they used to have their purpose. – Giovanni Botta Apr 07 '15 at 15:30
  • 4
    "I have no idea whether that case is exercised frequently enough for the optimization to be important" -- ArrayList uses copyOf for resizing. That said, Arrays.copyOf is intrinsified (recognized and treated specially by the compiler) in recent HotSpot versions, so the source code is not really relevant. – Jeffrey Bosboom Apr 07 '15 at 15:38
  • 1
    `(T[]) new Object[newLength]` doesn't work if `T` is not `Object`, or more accurately -- it won't throw a ClassCastException in `Arrays.copyOf`, but it probably _would_ throw a ClassCastException when the caller tried to actually use it as e.g. a `String[]`. This appears to be just an optimization, as you described. – Louis Wasserman Apr 07 '15 at 16:52
6

First of all, the cast in that line

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

are absobuletly needed. Removing them will result in a compilation error :

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

Now to answer your question What is this line checking ?

It simply verify if the given array is of object type, which is part of the answer for your other question Why Array.newInstance not good enough for both cases?

In the first case, we already know that the array is of Object type so there is no point in calling the newInstance method to retrieve the correct type, this would only result in performance loss.

As for your final example,

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

That it does compile, it is true. Because the given arguments to the method are all valids. It will certainly fail at runtime; what would be the expected output of converting "a" to Integer type ?

Now, when to use copyOf ? When you already know the both types, and already know that they are valid together.

It main usage is to return a copy but truncated or padded with [null/default values] to the original array.

Jean-François Savard
  • 20,626
  • 7
  • 49
  • 76
  • You say that Object casts are absolutely needed. Can you please explain in what case? Intellij tells me they are redundant. – Coder-Man May 29 '18 at 21:31
5
  1. It is checking if newType is array of Objects or not:

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

Why to do that? Because new Object[n] is faster than Array.newInstance

  1. Array.newInstance(Class<?> componentType, int... dimensions) creates an array of types defined by the first argument, eg String.class -> String[]. Note that String[].class.getComponentType() returns String.class

  2. You cannot use it like that, but it can be like this

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

in this case it depends only on actual type of elements, eg

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

will fail, you cannot write in Integer[] anything but Integer

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
1

Let me attempt to answer this:

To answer your first question, it's checking if the newType type is the same as the types in the array. Both are also upcasting the types to an Object type. That is, it tries to see if the array's parent type is of 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.

It would be helpful to notice that

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

is actually one line. i.e it is actually an if-else conditional.

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

Simple example here

So essentially that line would be the same as:

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

The reason why creating a new instance in Array.newInstance is likely a performance choice, where if the program always has to create new instances it'll be more expensive than just directly initializing a generic Object array and copy things over.

Arrays.copyOf will create a new array (with references to the old one) but with a newer length and pads the unused positions with empty objects. This is what it does on an array of ints in which it pads the unused indexes with zeros.

Arrays.CopyOf serves to provide a shallow-copy of the objects, that is it refers to the old items, but in a new array. This SO question has more info on it.

Community
  • 1
  • 1
matrixanomaly
  • 6,627
  • 2
  • 35
  • 58