Xin
Xin

Reputation: 1259

Do not understand the source code of Arrays.copyOf

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) 
    

Upvotes: 45

Views: 5106

Answers (5)

Radiodef
Radiodef

Reputation: 37875

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).

Upvotes: 24

Jean-Fran&#231;ois Savard
Jean-Fran&#231;ois Savard

Reputation: 21004

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.

Upvotes: 7

Evgeniy Dorofeev
Evgeniy Dorofeev

Reputation: 136072

  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

Upvotes: 6

matrixanomaly
matrixanomaly

Reputation: 6967

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.

Upvotes: 1

John Bollinger
John Bollinger

Reputation: 181149

  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.

Upvotes: 14

Related Questions