Returning an array of generic type

Click For Summary
The discussion revolves around implementing a method to return an array of common items from two generic arrays while adhering to specific constraints. The method must not change its header, and it cannot utilize Java's collection classes, requiring the use of java.lang.reflect.Array for array creation. Initial attempts to create a generic array resulted in NullPointerExceptions and ClassCastExceptions due to type erasure. A successful solution involves using Array.newInstance to create an array of the appropriate type based on the first element of the common elements list, ensuring that the method returns a correctly typed array. The final implementation handles null inputs and correctly populates the common items array.
Enharmonics
Messages
29
Reaction score
2
Moved thread to homework forum
This is a homework problem, so I have to follow some stipulations, namely:

Do not change the method header in any way.
Do not use the classes Arrays, Collections, Set, or Map.
You can use the java.lang.reflect.Array class. Use equals to
test if two items are equal.

The method

Java:
public static <T>
T[] commonItems( T[] arr1, T[] arr2)

returns an array that contains the items that occur in both array1 and array2. Assume that the 2 arrays have no duplicate items. If one or both arrays are null, the output is the null array. If the 2 arrays have no atoms in common, commonItems returns the empty array.

The problem is, I have no idea how to get a method to return a generic-type array. Because of type erasure, I can't create an instance T[] of a generic array, empty or otherwise.

The only way I was able to do it was by setting the generic array reference variable to null. My first plan was to do that, then use an ArrayList of generic type to store the elements arr1 and arr2 have in common, and finally use the toArray() method to convert it into a "generic-type" array, which would be stored in the null-valued T[] reference variable, something like this:

Code:
public static <T>
T[] commonItems( T[] arr1, T[] arr2)
{
      ArrayList<T> commonElements = new ArrayList<>();
      T[] commonItems = null;

      // Code for finding the common elements goes here

     return commonElements.toArray(commonItems);
}

The above code results in a NullPointerException being thrown (presumably because commonItems was initialized to null and not an array of any kind)

Next I tried circumventing the "no generic array instantiation" rule by creating an array of type Object[] and casting it into a "generic type" array:

Code:
      ArrayList<T> commonElements = new ArrayList<>();
      T[] commonItems = (T[]) new Object[]{};

      // Code for finding the common elements goes here

     return commonElements.toArray(commonItems);

Which results in a ClassCastException.

I googled around and checked on Stack Overflow for a while about how to return generic-type arrays, and tried my own implementation of the closest thing I could find to a workaround, which basically involves writing a "conversion" method and using Array.newInstance() to make the generic array to be returned:

Java:
public static <T>
    T[] commonItems(T[] arr1, T[] arr2)
    {
       
        // commonElements will store the elements
        // that occur in both arr1 and arr2
  
        ArrayList<T> commonElements = new ArrayList<>();
        T[] commonItems = null;
       
        // If one or both arr1 and arr2
        // are null, the output is the
        // null array
       
        if (arr1 == null || arr2 == null)
        {
            return commonItems;
        }
       
        // Nested enhanced for loops
        // used to iterate through the
        // two arrays
       
        for (T firstElement : arr1)
        {
            for (T secondElement : arr2)
            {
                // If the two elements are
                // equal, add it to
                // commonElements
               
                if (firstElement.equals(secondElement))
                    commonElements.add(firstElement);
            }
        }
       
        // Return an array containing the
        // common items in arr1 and arr2
       
        return convertToArray(commonElements, arr1.getClass());
    }
   
    // HELPER METHOD FOR commonItems METHOD
   
    public static <T>
    T[] convertToArray(ArrayList<T> commonElements, Class<? extends Object[]> c)
    {
        // Use the newInstance() method to
        // instantiate an array, then cast
        // it to a generic type.
       
        T[] commonItems = (T[]) Array.newInstance(c, commonElements.size());
       
        // Now use the toArray() method
        // to convert commonElements
        // into an array of "generic"
        // type
       
        commonItems = commonElements.toArray(commonItems);
       
        // Return the array
       
        return (T[]) commonItems;
    }

Which results in an ArrayStoreException at the line

Code:
commonItems = commonElements.toArray(commonItems);

I know that generic arrays (like T[]) would be converted to Object[] type arrays at runtime due to type erasure, but because of the method header (which I'm not allowed to change) the method won't run unless I return an array of type T[].

I'm guessing the idea behind the method is for the "generic array" to be of the same type as arr1 and arr2, which was my reasoning for passing arr1.getClass() to the convertToArray method and trying to use it as an argument for the newInstance() method.

But I don't know what else to try at this point.
 
Physics news on Phys.org
You created an array of arrays because your variable c was the class of T[] instead of the class of T.
Anyway I think I got it to work. Try this.
Java:
T[] commonItems(T[] arr1, T[] arr2)
{
    ArrayList<T> commonElements = new ArrayList<>();
    T[] commonItems = null;

    if (arr1 == null || arr2 == null)
    {
        return commonItems;
    }

    for (T firstElement : arr1)
    {
        for (T secondElement : arr2)
        {
            if (firstElement.equals(secondElement))
                commonElements.add(firstElement);
        }
    }

    if(commonElements.size() > 0) {
      commonItems = (T[]) Array.newInstance(commonElements.get(0).getClass(), commonElements.size());
      commonElements.toArray(commonItems);
    } else if(arr1.length > 0) {
      commonItems = (T[]) Array.newInstance(arr1[0].getClass(), 0);
    } else if(arr2.length > 0) {
      commonItems = (T[]) Array.newInstance(arr2[0].getClass(), 0);
    } else {
      commonItems = (T[])arr1.clone();
    }
    return commonItems;
}
 
Last edited:

Similar threads

  • · Replies 18 ·
Replies
18
Views
2K
  • · Replies 2 ·
Replies
2
Views
2K
Replies
4
Views
2K
  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 6 ·
Replies
6
Views
3K
  • · Replies 4 ·
Replies
4
Views
3K
  • · Replies 2 ·
Replies
2
Views
4K
  • · Replies 3 ·
Replies
3
Views
3K
  • · Replies 6 ·
Replies
6
Views
2K
  • · Replies 11 ·
Replies
11
Views
12K