Extract array encapsulated in an Object - java

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.

Related

Method to flatten generic array

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"].

Convert any array of any type into string

I have a value of type Object. It could be of any type boolean, string, integer, long, etc. I want to convert that object into a string. I do use toString method and works just as supposed to. However, sometimes the object could be String[] or Integer[] etc. (basically an array). The problem is when converting them to string ,I get a coded, not readable string. I used Arrays.toString but it accepts only arrays and my value is of type Object and I've used String.join(" ", value) but this needs a type casting for the value to string and not all types can be cast into strings.
Is there a possible way to convert them to string no matter what the type is?
Note: I know that I can specify for each value an if statement and check the type but I don't think it's good way.
Write a utility method like below:
public static String convertToString(Object input){
if (input instanceof Object[]) {
// deepToString used to handle nested arrays.
return Arrays.deepToString((Object[]) input);
} else {
return input.toString();
}
}
Please note that the first if condition would be evaluated to false if the input is a primitive array like int[], boolean[], etc. But it would work for Integer[] etc.
If you want the method to work for primitive arrays, then you need to add conditions for each type separately like:
else if (input instanceof int[]){
// primitive arrays cannot be nested.
// hence Arrays.deepToString is not required.
return Arrays.toString((Object[]) input);
}
You can use map() on your array after converting it to a list as:
List<Integer> list = Arrays.asList(3, 6, 9, 12, 15);
list.stream().map(element -> element.toString()).collect(Collectors.toList());
String result = String.join(",", list);
The good thing about using map() is that it comes with the flexibility of processing and filtering your data.
Try this to convert Object type of array to array of string
Object[] src = { 5.6, "Rupesh", 5, "India" };
String[] dest = new String[src.length];
for(int i = 0; i < src.length; i++){
dest[i] = src[i].toString();
}
System.out.println(Arrays.toString(dest));

How to write a generic function which accepts arrays of literals

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);
}
}
}

Create Static Array from dynamic array with Generics [duplicate]

This question already has answers here:
why does List<String>.toArray() return Object[] and not String[]? how to work around this?
(5 answers)
Closed 3 years ago.
I want to create a static array from a dynamic array of whatever generic type the dynamic array was. I saw List#toArray() which returns Object[] and it doesn't use generics. Is it just safe to cast it to T[] or does the entire array have to be instantiated from the type of class using it?
I went on to try and create my own method in case java didn't provide one but, I got stuck with a compile errors
public static <T> T[] toArray(List<T> list)
{
T[] li = (T[]) Array.newInstance(T.class, list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
First of all, you don't need that method. You can use:
List<String> list = new ArrayList<>();
list.add("ff");
list.add("bb");
String[] array = list.toArray (new String[list.size ()]);
In order for your method to work, you have to pass the Class of the generic type parameter:
public static <T> T[] toArray(List<T> list, Class<T> clazz)
{
T[] li = (T[]) Array.newInstance(clazz, list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
Then you can call the method with:
String[] array = toArray(list, String.class);
The method proposed by Eran doesn't work if you have a generic element type, because you can't get a Class<List<T>>, say.
Instead, pass an IntFunction<T[]>:
public static <T> T[] toArray(List<? extends T> list, IntFunction<T[]> arraySupplier)
{
T[] li = arraySupplier.get(list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
Or, easier, use streams:
return list.stream().toArray(arraySupplier);
Then call like:
String[] array = toArray(list, String[]::new);
List<List<String>> listOfLists = ...
List<?>[] arrayOfLists = toArray(listOfLists, List<?>::new);
Notice that whilst this does support generic array elements, you can only create arrays with a reified element type, so your array type has to be List<?>[]; it still can't be List<String>[].
If your business requirement/Use Case requires an array to be no longer dynamic then you should first create a static array of size equal to your size of dynamic array.
ArrayList<Integer> al = [............] // assuming that ArrayList named al is having some data
int[] arr = new int[al.size()];
// from here you can use a for loop and initialize your static array
for(int i=0; i<arr.length;i++) {
arr[i] = (int) al.get(i); // Unboxing will also be done but still you can type cast to be on safe side
}
// Now you can de-reference the ArrayList object and call garbage collection which will wipe it out of the Heap Memory of your JVM.
al = null; // de-referencing the object by making the reference variable null
System.gc(); // GC happens periodically but to boost performance you can explicitly call it right away.
You can create a method accepting the list of objects and can handle all sorts of arrays using instanceof operator.

Casting Object[] to String[] error

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[]

Categories

Resources