I have a LinkedList<T> which contains Object toArray() method:
public Object[] toArray()
{
Object[] array = new Object[size];
int c=0;
for(Node<T> i = first;i != null;i=i.next)
{
array[c++] = i.data;
}
return array;
}
I would like to sort my LinkedList with a generic method: <T extends Comparable> void sort (List<T> list). To sort lists, i must represent them as array in this method:
T[] elements = (T[])list.toArray();`
However, i get ClassCastException at this line and i don't know why. Since the generic type of the method equivalent to the element's runtime type in the returned array, this cast is not a lie!
toArray() returns Object[]. The type information is lost and you can't cast it back to T[]. If you want to keep the type information you can use the following. Then you give the method an predifined array which get filled. If you don't give anything - toArray will create a new Object[].
T[] elements = list.toArray(new T[list.size()]);
just filling an array (another writing style):
T[] elements = new T[list.size()];
list.toArray(elements);
or if you use Java 8:
T[] elements = list.stream().toArray(T[]::new);
The method LinkedList.toArray() creates a new array of the type Object[]. It does not create a new array of the type T[]. This is important, because even though the array only contains instances of T, you cannot assign the array to a variable of the type T[], because the array itself has the type Object[]. You can reproduce this error with the following code:
String[] array = (String[]) new Object[0];
If I understand you correctly, you want to convert the list into an array to be able to implement your own search function. Given a List<T>, that contains elements of the type T you want to convert this list into an array of the type T[]. However, you cannot simply call new T[list.size()], since Java looses the generic type information at compile time. To create the correctly typed array, you need to use the reflection method Array.newInstance().
Here is an example:
#SuppressWarnings("unchecked")
private <T extends Comparable<T>> void sort(List<T> list, Class<T> clazz) {
T[] array = list.toArray((T[]) Array.newInstance(clazz, list.size()));
// sort array and write result to the list
}
And here the usage:
List<String> list = new LinkedList<String>();
// populate the list
sort(list, String.class);
System.out.println(list); // -> the sorted list
You should use T[] toArray(new T[list.size()]) instead. No need to cast.
Related
I am trying to understand generics in Java.
private List<Own> l = new ArrayList<Own>();
I have the following error :
no instance of Typed array variable T exist so that List<Own> conform to T[]
when I pass it in a method (readTypedArray) that expects T[].
private List<Own> list = new ArrayList<Own>();
private OwnParceable(Parcel in) {
in.readTypedArray(list, CategoriesParceable.CREATOR);
}
The method in.readTypedArray() expects an array T[], but you passed a List<Own which is not an array.
List is not an array you can't use it where an array is expected, List is an interface which extends Collection while array is a data structure in Java, check Difference between List and Array for further details.
You can either declare an Own[]instead of List<Own> or convert this list into an array before passing it to the method, check Convert list to array in Java:
in.readTypedArray(list.toArray(new Own[list.size()]), CategoriesParceable.CREATOR);
This has nothing to do with generics - Lists and arrays are just two different things. If your method expects an array, you need to pass it an array, not a List:
Own[] arr = new Own[10]; // Or some size that makes sense...
in.readTypedArray(arr, CategoriesParceable.CREATOR);
There is a possibility to create an array filled with content of specified List. To achieve that you can call method toArray() of your list reference, for example:
Integer[] array = list.toArray(new Integer[list.size()]);
I was working on Collection framework in Java , where i encountered a strange problem .
I made 2 lists of Strings 1 with the help of ArrayList while second was made using Arrays.asList(T ...).
After creation of these two list i tried to convert these lists into String arrays with the list.toArray() ,
as list.toArray() method call returns an object array , so i had to explicitly cast to String[] .
After casting some strange behaviour is happening as :
Case #1 : ( where list was created using ArrayList) , gives runtime exception as
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
Case 2 : (where list as created using Arrays.asList(T ... ) ) runs fine .
here is the code
String [] str = null ,str1 = null ;
List<String> list = new ArrayList<String>();
list.add("a");
List<String> list1 = Arrays.asList("a");
str = (String[]) list.toArray(); // Runtime Exception
str1 = (String[]) list1.toArray(); // Runs Fine
An ArrayList is backed by an Object[]. A copy of that array is returned with toArray().
Returns an array containing all of the elements in this list in proper
sequence (from first to last element).
It make no guarantees on the type of array returned. But we know this from the exception's message. If you want it to return a String[], use the overloaded method provided for this reason.
str = list.toArray(new String[0]);
The cast becomes unnecessary.
The List implementation returned by Arrays.asList maintains a reference to the array (implicit or explicit) passed as its variable arity argument.
Returns a fixed-size list backed by the specified array.
The invocation
List<String> list1 = Arrays.asList("a");
creates a String[] and passes that as the argument to asList. This isn't specified clearly by the API documention, but backed by seems to indicate that it will return that same array. (Looking at the implementation, it returns a clone.) Since it is a String[], there is no error when casting and assigning it to a variable of that type.
In both cases, the appropriate solution is to use the overloaded List#toArray(T[]).
For fun, run the following and check the type of array that is returned.
List<String> list1 = (List) Arrays.<Object> asList("a");
System.out.println(list1.toArray().getClass());
Don't make assumptions. Always rely on the API documentation. If it isn't clear, try to find a better solution.
The different calls to toArray are returning arrays with different component types. You can see this by running the following code:
List<String> list = new ArrayList<String>();
list.add("a");
List<String> list1 = Arrays.asList("a");
System.out.println(list.toArray().getClass());
System.out.println(list1.toArray().getClass());
On any version of Java 8 or earlier, the result is
class [Ljava.lang.Object;
class [Ljava.lang.String;
Basically this output means Object[] and String[] respectively.
However, this is a bug. See JDK-6260652. Although it's not stated very clearly, Collection.toArray() must return an Object[] and not an array of some subtype of Object. There are a couple reasons for this.
First is that Collection.toArray() was introduced in JDK 1.2, long before generics were added to the language. There was no possibility of any collection implementation returning anything other than Object[], so for compatibility, all collections' toArray() implementations must return Object[].
The second reason is that a rather offhand comment in the specification for toArray(T[]) says:
Note that toArray(new Object[0]) is identical in function to toArray().
which again requires toArray() to return Object[] and not an array of some other type.
This bug has been fixed in JDK 9. Running the code snippet above on a recent JDK 9 build gives the following output:
class [Ljava.lang.Object;
class [Ljava.lang.Object;
The fact that Arrays.asList("a") uses a String[] for internal storage is an implementation detail. The bug where toArray() returned something other than Object[] is this implementation detail leaking out. (In fact, the array is created by the varargs machinery, using the method's type parameter as the array component type. Arrays.asList() just wraps the array it's given.)
As others have said, if you want to control the component type of the returned array, use the other overload toArray(T[]):
String[] array = list.toArray(new String[0]);
String[] array1 = list1.toArray(new String[0]);
List<String> list = new ArrayList<String>();
list.add("a");
str = (String[]) list.toArray();
In this case your list invoke method toArray() of ArrayList class. Which looks like below, it returns Object []:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
And elementData declare:
transient Object[] elementData;
And constructor method:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
And DEFAULTCAPACITY_EMPTY_ELEMENTDATA:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
There for, elementData is totaly Object [] and can't be casted to any type, String etc...
With Arrays.asList(T...), it returns java.util.Arrays$ArrayList class. And java.util.Arrays$ArrayList also has toArray() method. That subtle toArray() method makes some confuse :). Here is its implementation:
public Object[] toArray() {
return a.clone();
}
And finally a field declare:
private final E[] a;
java.util.Arrays$ArrayList.toArray() able to return Object [] and actually E []. Hope this will help you :)
The key here is that Arrays.asList(..) does not return a java.util.ArrayList, but instead it returns a java.util.Arrays$ArrayList. So the .toArray() methods vary slightly.
If you want the first case to return a String[], you can change the call to
str = list.toArray(new String[0]);
I would like to understand the relationship between generics and arrays finally, so i will provide an example which is inconsisent for me, based on an ArrayList<T> :
Object[] elementData = new Object[size];
This is where elements of the generic list are stored.
public void add(T element){ elementData[size++] = element; }
public T get(int index) { return (T)elementData[index] }
Completely works. I can get out the underlying <T> objects, however the array which contains the references to these objects is Object.
In contrast to this:
public Object[] toArray()
{
Object[] result = new Object[size];
for(int i = 0;i<size;i++)
{
result[i] = elementData[i];
}
return result;
}
I cannot cast the elements in the returned array to their real type, however the whole set up is the same: an Object array which contains references to <T> objects. I got ClassCastException error, when trying to cast the elements to their real type.
If you look into the Collection interface, you see that there are 2 toArray() methods:
Object[] toArray()
Returns an array containing all of the elements in this collection.
<T> T[] toArray(T[] a)
Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array.
The reason for that is that you cannot make generic arrays, so you only return a Object[].
Making a generic method of your toArray() is simple:
public <T> T[] toArray(T[] arr)
{
T[] result = arr.size == size ? arr : (T[])java.lang.reflect.Array
.newInstance(arr.getClass().getComponentType(), size);
for(int i = 0;i<size;i++)
{
result[i] = elementData[i];
}
return result;
}
Arrays know their component type at runtime. Therefore, to create an array, you need to provide the component type at runtime. However, in your case you don't know T at runtime. Therefore, you cannot create something that is truly a T[]. You can only return Object[], or rely on someone giving you something with which you can get the type T from at runtime.
You can cast the elements in the returned array to their real type.
But you cannot cast the array to T[ ]
Arrays in java existed before java 5, which introduces Generics.
So Arrays doesn't rely on generics, this is why we often use Collections instead of arrays in API.
So Collection can eventually be casted to Collection, but Object[] cannot be casted to another array T[], it would be another incompatible type.
I think it has to do with the fact that arrays are covariant and thus you are allowed to cast from T[] to Object[], since Object can do anything T can, but the other way around does not make sense, Object can not do everything T can.
I'm trying to order a binary search tree, and store its values on an array, but when I try to convert my ArrayList to array it says I cannot convert an Object to Comparable.
#Override
public T[] postOrder() {
ArrayList<T> array = new ArrayList<T>();
postOrder(root, array);
return (T[]) array.toArray();
}
private void postOrder(BSTNode<T> currRoot, ArrayList<T> array) {
if (currRoot == null)
return;
if (!currRoot.getLeft().isEmpty())
postOrder((BSTNode<T>) currRoot.getLeft(), array);
if (!currRoot.getRight().isEmpty())
postOrder((BSTNode<T>) currRoot.getRight(), array);
array.add(currRoot.getData());
}
The error message:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
edit: solved that way
public T[] postOrder() {
ArrayList<T> array = new ArrayList<T>();
postOrder(root, array);
return array.toArray((T[]) Array.newInstance(root.getData().getClass(), size()));
}
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
This means you are trying to cast Object[] to Comparable[]. (The "[L" means array.)
toArray returns Object[]. You may be able to use <T>toArray(T[]) instead; however, due to type erasure you cannot do
// cannot do
// vvvvv
return array.toArray( new T[ mySize ] );
So either
Your tree needs a Class<T>.
The caller needs to pass a T[] to fill.
postOrder needs to return a non-generic array. (Object[]... Comparable[]...)
You should return a List<T> instead of a T[].
I notice you are using #Override so maybe your supertype (interface, superclass) has instructions on how to implement this method. (i.e. if this is homework you should ask your instructor because it is not clear to me which solution you should use.)
If you happen to have a Class<T>, then you can do
#Override
public T[] postOrder() {
ArrayList<T> array = new ArrayList<T>();
postOrder(root, array);
return (T[]) array.toArray(
java.lang.reflect.Array.newInstance(myClassT, array.size())
);
}
Otherwise you need to change the signature of the method.
See also
How to create a generic array in Java?
Generic arrays are messy, you need to call the other toArray() method:
public T[] postOrder(T[] result) {
ArrayList<T> array = new ArrayList<T>();
postOrder(root, array);
return array.toArray(result);
}
It doesn't matter what size the result is, just make sure you don't use later what you passed in, because it may get reallocated inside toArray if the size is not correct, so use only the return value of postOrder.
Consider that it's probably much much more cleaner if you just return List<T>, and let the caller take care of converting it to an array! Notice how much less explanation is needed for returning a List...
public List<T> postOrder() {
ArrayList<T> array = new ArrayList<T>();
postOrder(root, array);
return array;
}
I am trying to make a generic function into which I pass an array (int[], String[]) and gets back the sorted version of it using TreeSort. I haven't even had the chance to begin to debug it since the mainclass refuses to accept an int[] array as the parameter.
TreeSort
public T[] treeSort(T[] arr) {
T[] result = (T[]) new Object[arr.length];
for (int i = 0; i < arr.length; i++)
if (arr[i] != null)
add(arr[i]);
Iterator it = iterator();
int count = 0;
while (it.hasNext())
result[count++] = (T) it.next();
return (T[]) result;
}
Main
BinarySearchTree<Integer> tree2 = new BinarySearchTree<>();
int[] unsortedNums = {6,3,7,4,1,2,9,8,5};
int[] sortedNums = tree2.treeSort(unsortedNums);
Error
The method treeSort(Integer[]) in the type BinarySearchTree is not applicable for the arguments (int[])
So I attempted to change unsortedNums and sortedNums from int to Integer (why should this matter?), and now running it yields this error:
Errorr 2
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
on line
T[] result = (T[]) new Object[arr.length];
In a generic method in Java, it is not allowed to create an array of the generic type. You are trying to circumvent this rule by creating an Object array and casting it to the generic type, but the compiler is too smart and prevents this. The reason for this rule is that the generic type could well be an Interface, so the runtime wouldn't know which class to create.
For your example, if you really need to keep the unsorted array as it is and return a copy (usually, sorting happens in-place so you would just edit the original array), you can either let the user pass the type of the generic (as in sp00m's answer), or let the user supply the destination array as a parameter so you don't need to create an array yourself.
IMO, the best practice here would be to make your sorting happen in place instead of returning a sorted copy of the data.
Instead of:
T[] result = (T[]) new Object[arr.length];
You have to use:
T[] result = (T[]) Array.newInstance(clazz, arr.length);
Where clazz is the type of the nested elements:
public T[] treeSort(T[] arr, Class<T> clazz) {...}
Finally, when calling, use the boxed primitive:
Integer[] unsortedNums = { 6, 3, 7, 4, 1, 2, 9, 8, 5 };
Integer[] sortedNums = treeSort(unsortedNums, Integer.class);
In Java, arrays know their component types at runtime (the component type is part of the actual runtime array type), and thus you need to specify the component type at runtime to create an array of that type.
#spoom's answer showed you how to create the array given a class object passed into the function. However, you actually already have that information without passing it separately. The parameter arr is of type T[], which means the array object's actual runtime type must be T[] or a subclass thereof. We can extract the component type from that and use it to create the new array:
T[] result = (T[]) Array.newInstance(arr.getClass().getComponentType(), arr.length);
This is how, for example, Arrays.copyOf() and related functions work.
You can't use primitive types (int) in generitcs. Try to use object wrapper, for example
Integer[] unsortedNums = {6,3,7,4,1,2,9,8,5};
Integer[] sortedNums = tree2.treeSort(unsortedNums);
Combining Generics and arrays is problematic in Java; check out the article Restrictions on Generics for details.
As to solving the problem, the easiest thing to do would be not using an array but rather a List. So for example:
public List<T> treeSort(List<T> list) {
List<T> result = new ArrayList<T>(list.size());
for (int i = 0; i < list.size(); i++)
if (list.get(i) != null)
result.add(list.get(i));
Iterator<T> it = list.iterator();
int count = 0;
while (it.hasNext())
result.set(count++, it.next());
return result;
}
Mind you, I haven't tested the logic.