How to make a generic method that merges arrays of any type?

Viewed 166

I have a method that takes two arrays and merges them with elements in natural order. I was wondering if it is possible to make it generic so it can take arrays of any type and merge them into an array of the same type?

Right now I'm able to construct only an array of Object

public static void main(String[] args) {

    Integer[] i1 = {1, 3, 5, 7, 9};
    Integer[] i2 = {2, 4, 6, 8, 10, 12, 14};

    String[] s1 = {"A", "C", "E", "G"};
    String[] s2 = {"B", "D", "F"};

    System.out.println(Arrays.toString(mergeAndSortArrays(i1, i2)));
    System.out.println(Arrays.toString(mergeAndSortArrays(s1, s2)));
}

public static<T extends Comparable<T>> Object[] mergeAndSortArrays(T[] a, T[] b) {
    final Object[] merged = new Object[a.length + b.length];

    int aPos = 0, bPos = 0, curIndex = -1;

    while (++curIndex < merged.length) {

        int comp = a[aPos].compareTo(b[bPos]);
        merged[curIndex] = (comp < 0) ? a[aPos++] : b[bPos++];

        if (aPos == a.length) {
            while (bPos < b.length) {
                merged[++curIndex] = b[bPos++];
            }
            break;
        }

        if (bPos == b.length) {
            while (aPos < a.length) {
                merged[++curIndex] = a[aPos++];
            }
            break;
        }
    }

    return merged;
}
2 Answers

You can't create an array of generic type in Java, but you can use a creator lambda:

public static<T extends Comparable<T>> T[] mergeAndSortArrays(
    T[] a, 
    T[] b, 
    IntFunction<T[]> arrayCreator
) {
    final T[] merged = arrayCreator.apply(a.length + b.length);

    ...
}

and then:

mergeAndSortArrays(i1, i2, Integer[]::new)
mergeAndSortArrays(s1, s2, String[]::new)

Or alternatively, you have Class<T> itemClass as method parameter (instead of the lambda) and then use (T[]) Array.newInstance(itemClass, a.length + b.length) to create new array.

If you want to avoid additional method arguments (like creator lambda or item class), you can use reflection to retrieve array item type:

final Class<?> itemClass = a.getClass().getComponentType();
final T[] merged = (T[]) Array.newInstance(itemClass, a.length + b.length);

And of course you can also use an existing library for this, like Apache Commons:

final T[] merged = org.apache.commons.lang3.ArrayUtils.addAll(a, b);
// ...

If you create the mergedArray out of the type from a, it can look like this.

public static <T extends Comparable<T>> T[] mergeAndSortArrays(T[] a, T[] b) {
    final T[] merged = (T[])Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
Related