JavaData & JavaCopying Arrays in Java 6

Copying Arrays in Java 6

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The Java language contains some wonderful antiquities. Switch statements, for example, still don’t support the notion of switching on String objects. (The switch statement has, however, been updated to support enum types.) The reason for this appears to date back to the notion of how switch statements were originally designed in C. Whatever the reason, I find it unfortunate that I can’t write switch statements around Strings, something that I’ve wished I could do a few times.

System.arraycopy, the method used to rapidly copy the contents of one array to another, appears to have similar legacy underpinnings. Even the name of the method looks like an old style C function such as memcpy. This preferred means of copying arrays in Java hasn’t changed since the first release.

The arraycopy method, defined as a static method on System, is certainly powerful enough. The System class provides a single general-purpose method:

System.arraycopy(sourceArray, sourceStartIndex, targetArray, targetStartIndex, length);

The arraycopy method supports more than just copying the entire contents of a source array into a target. In addition, arraycopy allows for a source and target start index, as well as a length that represents the number of elements to copy.

This additional flexibility, while it may not be what I need most of the time, is commonly used. The ArrayList class, for example, uses arraycopy to shift the contents of an array when someone inserts an element before its last element. Sun implemented arraycopy to be very fast: It’s not implemented in Java, like most of the API, but instead as a native operation that gets executed directly by the JVM.

Most of the time, though, I want to copy an entire array, not subsections. Nor do I need to shift elements in an array very often. Most of my invocations of System.arrayCopy require two lines of code:

      String[] source = { "alpha", "beta", "gamma" };
      String[] target = new String[source.length];
      System.arraycopy(source, 0, target, 0, source.length);

For this simple, common need, it seems like Java requires too much code. If I use this construct a couple or more times in a given application, I want a nice reusable method:

private final <T> T[] copy(T[] source) {
   T[] target = new T[source.length]; // This will not compile!
   System.arraycopy(source, 0, target, 0, source.length);
   return target;
}

Well, that won’t work. Compilation fails with the error message:

Cannot create a generic array of T

I can get past this error using reflection. Here’s the code that makes the copy method work:

private final <T> T[] copy(T[] source) {
   Class type = source.getClass().getComponentType();
   T[] target = (T[])Array.newInstance(type, source.length);
   System.arraycopy(source, 0, target, 0, source.length);
   return target;
}

The Java compiler shows some concern about the call to newInstance, giving me the warning “Type safety: the case from Object to T[] is actually checking against the erased type Object[].” Using reflection means that there’s not sufficient information for the compiler to know whether or not this is a bad cast. While there might be a better solution, I take the simple route and suppress that pesky warning:

@SuppressWarnings("unchecked")
private final <T> T[] copy(T[] source) {
   Class type = source.getClass().getComponentType();
   T[] target = (T[])Array.newInstance(type, source.length);
   System.arraycopy(source, 0, target, 0, source.length);
   return target;
}

Using this copy method, my client code is as simple as it should be:

String[] source = { "alpha", "beta", "gamma" };
String[] target = copy(source);

As demonstrated, I can build my own general-purpose array copy utility method in short order.

Introducing copyOf

In Java 6, I no longer need to roll my own utility method: Sun has introduced direct support for copying an array. Sun has defined a new overloaded method, copyOf, on the Arrays class.

The basic form of copyOf is:

targetArray = Arrays.copyOf(sourceArray, length);

I wrote a number of JUnit tests to demonstrate behavior of the copyOf method (and of its variants). Here’s the first test, showing basic functionality:

@Test
public void genericArrayCopyOf() {
   Number[] source = { new Double(5.0), new Double(10.0) };
   Number[] target = Arrays.copyOf(source, source.length);
   assertEquals(source, target);
}

One nice new feature of JUnit 4 is its ability to compare two arrays using assertEquals. When doing so, JUnit first compares the lengths of each array. If both array lengths are equal, JUnit compares each element in the array using equals. If the arrays differ, JUnit provides a failure message that shows the unequal values.

Java overloads copyOf to support primitive types, as well as a couple other variants. Another test shows how I can supply a source range:

@Test
public void copyOfWithRange() {
   String[] source = { "0", "1", "2", "3", "4" };
   String[] target = Arrays.copyOfRange(source, 2, 4);
   assertEquals(new String[] { "2", "3" }, target);
}

Here is a test that demonstrates copying a primitive range:

@Test
public void primitives() {
   int[] source = { 0, 1, 2, 3, 4 };
   int[] target = Arrays.copyOfRange(source, 4, 5);
   assertEqualsPrim(new int[] { 4 }, target);
}

I wrote the method assertEqualsPrim because JUnit 4 does not cover comparing two arrays containing primitive objects. It contains only a version that compares two Object arrays. Comparing two primitive arrays using assertEquals means that Java looks for memory equivalence. No matter, assertEqualsPrim is pretty easy to write:

static void assertEqualsPrim(int[] expected, int[] actual) {
   if (expected.length != actual.length)
      fail(String.format("expected length = %s, actual length = %s",
            expected.length, actual.length));
   for (int i = 0; i < expected.length; i++) {
      if (expected[i] != actual[i])
         fail(String.format(
               "mismatch at index %d: expected [%s] but was [%s]", i,
               expected[i], actual[i]));
   }
}

What if I want the new array to be of a new, subclass type? The following test fails:

@Test
public void genericArrayCopyOfWithNewType() {
   Number[] source = { new Double(5.0), new Double(10.0) };
   Double[] target = (Double[])Arrays.copyOf(source, source.length);
   assertEquals(source, target); // fail!
}

But Java 6 allows me to declare a new type for the target array on a copy:

@Test
public void genericArrayCopyOfWithNewType() {
   Number[] source = { new Double(5.0), new Double(10.0) };
   Double[] target = Arrays.copyOf(source, source.length, Double[].class);
   assertEquals(source, target);
}

Performance and Alternatives

I can actually use the clone method to copy a source array into a target array, so technically copyOf isn’t necessary. However, my hastily written performance tests demonstrate that clone is fairly slow. They also demonstrate that copyOf is a mite slower than arraycopy. It’s probably not enough for me to worry about, so I’ll use the new copyOf method. As always, I’ll profile if necessary.

Is the new set of copyOf methods an essential addition to Java? Absolutely not. It could easily be looked at as syntactical sugar. There’s no reason I couldn’t continue to use arraycopy. However, it’s in line with simplifying efforts to code Java applications, something that Sun promoted heavily with the introduction of Java 5.

My take is that small simplifications add up dramatically. I’m pleased with how much simpler I can get my Java 5 applications to look than code written with earlier versions of Java. With Java 6, Sun has introduced only a tiny amount of core Java simplification. I’ll take it!

About the Author

Jeff Langr is a veteran software developer with a score and more years
of experience. He’s authored two books and dozens of published articles
on software development, including Agile Java: Crafting Code
With Test-Driven Development (Prentice Hall) in 2005. You can find out more
about Jeff at his site, http://langrsoft.com, or you can contact him directly at jeff@langrsoft.com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories