I need this code, but i get this error:
Ljava.lang.Object; cannot be cast to java.lang.String
public Object[] getAllKeys (){
return keys.toArray(new Object[keys.size()]);
}
public String[] getNames (){
return ((String[])super.getAllKeys()); <- Error here. Can't cast, why?
}
The type of the array is Object[] so it cannot know that it contains only Strings. It is quite possible to add a non-String object to that array. As a result the cast is not allowed.
You can return Object[] and then cast each of the objects within that array to string. i.e. (String)arr[0] or you can create a new String[] array and copy all the elements over before returning it.
toArray() returns an array of Objects. If you want to create an array of Strings out of it, you will have to do it yourself. For example,
Object [] objects = super.getAllKeys();
int size = objects.size();
String [] strings = new String[size];
for (int i = 0; i < size; i++)
strings[i] = objects[i].toString();
or something similar... Hope this is useful.
Every String is an Object. Every Object is NOT a String.
You cannot do the cast, because even though Object is a base class of String, their array classes Object[] and String[] classes are unrelated.
You can fix this problem by introducing an additional method that allows taking a typed array:
public Object[] getAllKeys (){
return getAllKeys(new Object[keys.size()]);
}
// Depending on your design, you may want to make this method protected
public <T> T[] getAllKeys(T[] array){
return keys.toArray(array);
}
...
public String[] getNames (){
return super.getAllKeys(new String[keys.size()]);
}
This code takes advantage of the other overload of toArray, which accepts a typed array as an argument.
This cannot be done implicitly since the runtime cannot know that the elements in Object[] are all String types.
If you don't want to code a loop yourself, then one way to coerce is to use
String[] myStringArray = Arrays.asList(keys).toArray(new String[keys.length]);
I think that this will happen without any string copies being taken: asList() binds to the existing array data and toArray uses generics which are removed at runtime anyway due to type erasure. So this will be faster than using toString() etc. Don't forget to deal with any exceptions though.
Try the following snippet
Object[] obj = {"Red","Green","Yellow"};
String[] strArray = (String[]) obj; // Casting from Object[] to String[]
Related
I have a method that takes an in an array and copies it in a random order into another array and returns the shuffled array.
However, if I want to make it generic, I can't create the second array of type E.
To get around this, I tried using an Arraylist and then using the .toArray() method and casting it to type E, but that returns an array of objects.
My current solution is to just modify the array directly and return that, but is there a way to return an array of the proper type, AKA the type of the array passed into the method?
import java.util.ArrayList;
import java.util.Arrays;
public class ShuffleArray
{
public static void main(String[] args)
{
String[] list = {"bob", "maryo", "john", "david", "harry"};
//doesn't work, can't store array of objects in array of strings
list = shuffle(list);
//works because I modify directly
shuffle(list);
}
public static <E> E[] shuffle(E[] list)
{
ArrayList<E> shuffledList = new ArrayList<>();
//shuffle the array
while (shuffledList.size() != list.length)
{
int randomIndex = (int)(Math.random() * list.length);
if (!shuffledList.contains(list[randomIndex]))
{
shuffledList.add(list[randomIndex]);
}
}
//overwrites the initial values of the array with the shuffled ones
for (int i = 0; i < list.length; i++)
{
list[i] = shuffledList.get(i);
}
//How do I make this return an array of type String?
return (E[]) shuffledList.toArray();
}
}
All arrays have a public clone() method, which returns the same type as the original array:
return shuffledList.toArray(list.clone());
You have a different problem, better described here: make arrayList.toArray() return more specific types
Change your return statement to the following
return shuffledList.toArray(E[]::new);
You could use Arrays.copyOf:
shuffledList.toArray(Arrays.copyOf(list, list.length));
although internally the method uses casting as well.
By the way, there is a built-in method Collections.shuffle. Maybe the best approach would be to work on Lists instead of raw arrays?
UPDATE: for the copyOf approach to work, you would need to bound the type parameter to Objects i.e. change it from <E> to <E extends Object>. The method wouldn't work for raw types (int, long etc.).
yeah why dont u just create an array of type E and store the values in it
E[] array = new E[list.length];
then just use that array to store the shuffled values and return it
I've been trying to write a method that will flatten a generic array if it is nested.
private static <T> List<T> flatten(T[] in) {
List<T> result = new ArrayList<>();
for (T e : in) {
if (e.getClass().isArray()) {
result.addAll(Arrays.asList(e)); ## Issue is here.
} else {
result.add(e);
}
}
return result;
}
This code does not cause any errors but also does not work. When e is not an array, things work as expected... a list is populated with the elements of in and returned.
However when e.getClass().isArray() == true, the elements of e are not added. Rather the original array is added so I end up with a list of arrays.
My use case here is that I have a method that is being passed generics T[] someArray
public <T> void doSomeStuff(T[] someArray) {
Set<T> unique = Sets.newHashSet(someArray)
... do some stuff with the unique values ...
}
The input someArray may either be nested or not (i.e. T itself may be an array, resulting in T[][]). I want to determine the unique elements contained in the input, whether or not it is nested. Passing the input someArray to a set only works if it's not nested, hence I'm trying to flatten.
So my question is, how can I do this and why is my method above not working? Thanks in advance for the edcuation.
Your code can't work. The generics just don't line up.
Let's say you have an array that is a combination of strings and arrays of strings. That cannot possibly be a T[] unless T is object, which isn't what you want (as that would mean you get a List<Object>. After all, If T is String, then your input array, which is defined as T[] in, is a String[] in, which cannot contain string arrays. After all, a String[] is not a subtype of String, for obvious reasons.
It is impossible to describe in terms of generics the concept of 'an array of Strings, or an array of arrays of Strings, or an array of arrays of arrays of Strings, and so forth'. So, generics have no place here. If you want that, all you can 'type' is 'an array whose component type is unknown and hybrid anyway', which is Object[] in java (this is co/contra-variance wise broken, but this is just part of the java spec: Variance on arrays is incorrect, known problem and not fixable).
This gets you a secondary issue: Generics are erased, and in that model you don't have an actual type to work with. In fact, because it is impossible to use generics to tell the compiler to do some type checking on the input array, there is nothing the compiler can do for you, so any type checking you want (and you clearly want that, you don't want to return a List of who knows what this is), will have to be done at runtime.
Unfortunately, it is impossible to do that, too - you can't check if at runtime if some object is, say, a Map<String, Integer>.
So, what you want is impossible.
It becomes possible if you're okay with this method being only able to do the job for reified types. That is, types that don't contain any <> themselves. So, if you want to take 'an array that contains a combination of "Map of string to integer" and "arrays of Maps of string to integer"', this method will not be able to do that and it is in fact completely impossible to do such a thing in java. But if you're okay with, say, "An array containing a combination of strings and arrays of strings" and want to turn that into a flattened-out list of strings, okay, that's possible.
It's complicated, though:
public <T> List<T> flattenArray(Class<T> type, Object[] in) {
if (type.isArray()) throw new IllegalArgumentException();
var out = new ArrayList<T>();
flattenArray0(type, in, out);
return out;
}
private <T> void flattenArray0(Class<T> type, Object[] in, List<T> out) {
for (Object a : in) {
if (a == null) {
out.add(null);
} else if (a.getClass().isArray()) {
flattenArray0(type, (Object[]) a, out);
} else {
out.add(type.cast(a));
}
}
}
In action:
Object[] test = new Object[3];
test[0] = "Hello";
test[1] = new String[] {"Foo", "Bar"};
Object[] threeDeep = new Object[2];
test[2] = threeDeep;
threeDeep[0] = "Goodbye";
threeDeep[1] = new String[] {"Baz"};
List<String> result = flattenArray(String.class, test);
System.out.println(result);
should print: ["Hello", "Foo", "Bar", "Goodbye", "Baz"].
I am unable to provide values of type int[], float[], etc. to a generic function. I get errors that say basically that float[] is the wrong type and Float[] is what the function actually takes.
Here's an example of a method I wrote, and I'm trying to give it values like new int[]{0,1} (created in library somewhere else).
private static <T> JSONArray encodeArray(T[] array) {
JSONArray arr = new JSONArray();
Collections.addAll(arr, array);
return arr;
}
Is it even possible to write my function signature to accept these arrays of literals?
I could go to the call site, and do a conversion of float[] to Float[], but I don't know how to do that either.
Is it even possible to write my function signature to accept these
arrays of literals?
It is possible, but the parameter type will have to be Object, because that is the only common superclass of "arrays of primitives" and "arrays of references" (e.g. it can't be Object[] since arrays of primitives are not subclasses of Object[]). (There are also some interfaces that all arrays implement, but I will ignore those for now.) Unfortunately, this means that you will lose type safety as the compiler will not be able to give an error at compile time if someone passes a non-array type in.
To do array operations on this Object value, you will need to use the methods in the reflection helper class java.lang.reflect.Array. So you can do something like this:
import java.lang.reflect.Array;
// ...
private static JSONArray encodeArray(Object array) {
JSONArray arr = new JSONArray();
for (int i = 0, n = Array.getLength(array); i < n; i++) {
arr.add(Array.get(array, i)); // primitives are automatically wrapped
}
return arr;
}
The method which accepts a generic array.
public <T> void printArray(T[] array){
for (T element: array){
System.out.println(element);
}
}
You can't use primitives in generic functions. When generic are compiled, you end up with Object[] in the above example as the implementing type. As int[] and byte[] etc, do not extend Object[] you cannot use them interchangeably even if the code involved would be identical (again generics are not templates)
Add on the solution from #Max-Reshetnyk, It is better if you check ArrayList for methods that help you add or remove... elements. Since primitive types are not meant to be used with generics, you should AutoBox them with their respective types and then use generics.
For instance:
class Main {
public static void main(String[] args) {
Integer[] x = new Integer[1];
x[0] = 1;
printArray(x);
}
public static <T> void printArray(T[] array){
for (T element: array){
System.out.println(element);
}
}
}
I'm trying to convert List type data collection into array.
public void method1(List s1){
List s=s1;
String []array = new String[s.size()];
array=(String[])s.toArray();
for(int i=0;i<array.length;i++){
System.out.println(array[i]);
}
}
Then following class cast exception occurs. Error line was "array=(String[])s.toArray();" line.
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
What is reason for that?
List.toArray() is a method in the interface List, and the documentation does not specify what kind of array is returned by the method. However, unlike other answers suggested, the List.toArray() method cannot return an array of the generic type of the list, because the generic type of a list is not known at runtime.
Because of that, all the implementations of List that are built-in to the standard API return an Object[], and not a String[].
However, you are allowed to pass your own pre-allocated array into the method List.toArray(Object[]). If you do that, you are guaranteed to get a return value that has the same element type as the array that you passed in. If the array that you passed in has the same size as the list itself, then that array will be used (otherwise, a new array of the proper size is allocated internally)
This will fix it:
public void method1(List s) {
String[] array = s.toArray(new String[s.size()]); // <-- pass the array as an argument
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
You didn't specify the generic type for list, and you declared array as array of Strings. This should work:
public void method1(List<String> s1){
List<String> s=s1;
String []array = new String[s.size()];
array = (String[])s.toArray(array);
for(int i=0;i<array.length;i++){
System.out.println(array[i]);
}
}
EDIT
Instead of List#toArray() which returns Object[], you should use List#toArray(T[] a) which returns the type passed in as a parameter.
Let's say i have an array encapsulated in an object :
String[] strvls = { "Alessio", "Ale" };
Object container = strvls;
I want to extract back the array from the container object, how can i do ?
I know that is an array checking in this way :
if(container.getClass().isArray()) {
//Extract the encapsulated array - How ?
}
I can't cast into a String array, because I can't know for sure that the type of the array was String[] ... So i need to cast into a generic type array, Is it possible in Java ?
You can cast it to Object[] array:
String[] strvls = { "Alessio", "Ale" };
Object container = strvls;
if (container.getClass().isArray()) {
Object[] data = (Object[]) container;
System.out.println(data.length);
}
Similar to Karaszi's answer, but a shortcut maybe:
if(container instanceof Object[])
System.out.println("Array size: " + ((Object[])container).length);
Important note: This will not work for primitive arrays. The code above works since String derives from Object class.
You could have such a method:
private static <T> T[] getArray(Object o){
return (T[]) o;
}
This will help work with a generic type and you can keep any 'unchecked' compiler warnings encapsulated in this method. Then you can just do:
Object[] array = getArray(container);
if you don't know anything about the type or :
String[] array = getArray(container);
if you know it's a string array.