Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)  
like image 761
Xin Avatar asked Apr 07 '15 15:04

Xin


People also ask

Can arrays copyOf () be used to make a true copy of an array?

With arrays of primitive data the Arrays. copyOf(..) method can't copy references as these are not used for primitives. It just copies the source element values into the target elements.

What is arrays copyOf?

Arrays. copyOf() method copies specified array into a new array with specified length. If length of new array is greater than that of source array, copyOf() method copies the whole source array to new array and pads the new array with zeros, at end, to match the specified length of new array.

Does System Arraycopy create new array?

arraycopy() simply copies values from the source array to the destination, Arrays. copyOf() also creates new array. If necessary, it will truncate or pad the content.


1 Answers

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

like image 60
Radiodef Avatar answered Oct 08 '22 02:10

Radiodef