Concatenate arrays of objectsFrom WikiJava
In this article I'll show and describe a simple method that allows you to concatenate several arrays of any object forming a single array out of them. The #Description paragraph explains the details of the method. while the #The full code paragraph contains a complete listing of an executable program showing how the method works
DescriptionThe thing looks like easy, but it presents some complexities, caused to the fact that in java you cannot instance an array of a parametric type (T). The heart of this example is the method static <T> T[] concat(T[]... arrays) which takes an arbitrary number of arrays of a generic type (not primitives), and returns a single array of the same type. Below the listing for the method: private static <T> T[] concat(T[]... arrays) { // count total number of items in the resulting array. int length = 0; for (int i = 0; i < arrays.length; i++) { length += arrays[i].length; } // unfortunately in Java it's not possible to use the following syntax // T[] result = new T[length]; // this makes a compilation exception T[] result = (T[])Array.newInstance( arrays.getClass().getComponentType().getComponentType(), length); int k = 0; for (int i = 0; i < arrays.length; i++) { for (int j = 0; j < arrays[i].length; j++) { result[k++] = arrays[i][j]; } } return result; } The first part counts the total number of items the final array will have (which is the sum of the lengths of all the arrays). We will use this number to generate the length of the result array. This wouldn't be strictly necessary, as we can use an ArrayList to dynamically expand, but it should potentially save some time. T[] result = (T[])Array.newInstance( arrays.getClass().getComponentType().getComponentType(), length); This part makes the T[] Object of the same type as the objects passed in the arrays. As mentioned above in Java it is not possible to make an array of a parametric type. for example: T[] result = new T[length] This would throw an exception at compilation time. This has to do with the reification of the arrays in Java. Basically the arrays carry run-time information about the type of their components. As a result it is not possible to make an array of a parametric type (ex. T) that is unknown at runtime. If we have the class object of the type T at runtime, however, we can create the T[] array using reflection, using the method Array.newInstance(). Where do we get the class object for T from? Well, we know the type of the "arrays" argument is T[][] (the T[][] is created by the compiler when it sees a variable number of T[] arguments). Unless someone is lying, this "arrays" object is guaranteed to have a runtime type of T[][]. We can get its class object, and then we can get its component type T[] by using the "getComponentType()" method of a class. We use it again to get the class object for T. Some people might think of simply using "arrays[0].getClass().getComponentType()". The reasoning is that arrays[0] (or any other element of "arrays") has type T[], and its component type is T. Although this might work in some circumstances, it doesn't always work; this is because (1) "arrays" might be empty (i.e. no arrays were passed. this is a minor case that can be dealt with by returning null or something), (2) "arrays[0]" might be null (in fact all of the array arguments could be null), or (3) "arrays[0]" might be a subtype of T[] (i.e. "arrays[0]" might have an actual runtime type of U[], where U is a subtype of T, thus U[] is a subtype of T[], and thus this array could be validly passed), but if we create a U[] object, T objects from later arrays would not be allowed inside, creating a problem. The rest of the method just populates the array with the objects from the arrays. The full codeFollows is a simple executable class, that creates several arrays of strings and integers, and concatenates them, printing the results. import java.lang.reflect.Array; import java.util.Arrays; public class concatenateArrays { /** * @param args */ public static void main(String[] args) { concatenateStrings(); concatenateIntegers(); } /** * */ private static void concatenateStrings() { String[] array1 = populateStringArray(10, 5); String[] array2 = populateStringArray(15, 15); String[] array3 = populateStringArray(10, 45); System.out.println("array1: " + toString(array1)); System.out.println("array2: " + toString(array2)); System.out.println("array3: " + toString(array3)); String[] concat = concat(array1, array2, array3); System.out.println("Strings concatenated: " + Arrays.toString(concat)); } private static void concatenateIntegers() { Integer[] array1 = populateIntegerArray(10, 5); Integer[] array2 = populateIntegerArray(15, 15); Integer[] array3 = populateIntegerArray(10, 45); System.out.println("array1: " + toString(array1)); System.out.println("array2: " + toString(array2)); System.out.println("array3: " + toString(array3)); Integer[] concat = concat(array1, array2, array3); System.out.println("Integers concatenated: " + Arrays.toString(concat)); } private static String[] populateStringArray(int number, int start) { String[] result = new String[number]; int curNumber = start; for (int i = 0; i < number; i++) { result[i] = "a" + curNumber++; } return result; } private static Integer[] populateIntegerArray(int number, int start) { Integer[] result = new Integer[number]; int curNumber = start; for (int i = 0; i < number; i++) { result[i] = curNumber++; } return result; } private static <T> T[] concat(T[]... arrays) { // count total number of items in the resulting array. int length = 0; for (int i = 0; i < arrays.length; i++) { length += arrays[i].length; } // unfortunately in Java it's not possible to use the following syntax // T[] result = new T[length]; // this makes a compilation exception T[] result = (T[])Array.newInstance( arrays.getClass().getComponentType().getComponentType(), length); int k = 0; for (int i = 0; i < arrays.length; i++) { for (int j = 0; j < arrays[i].length; j++) { result[k++] = arrays[i][j]; } } return result; } } |
