Create Static Array from dynamic array with Generics [duplicate] - java

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.

Related

Is it possible to return an array of type <E> in a method in Java rather than an array of objects when using Generics?

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

How to populate generic array to main

I've got a generic array class and I want to return an array in the main so I can use the sort method that I have ready in the main. I understand that the constructor has an array in it so I'm wondering if I can use that. Or do I need to set up a new method to return this.array ? Also it returns a generic array, how do I choose the type in main?
public class dynamicArray <T>{
private int index;
private T[] array;
public dynamicArray() {
array = (T[])new Object[10];
this.index = 0;
}
public T [] populate() {
return this.array;
}
Here I chose the integer type for the class. I'm not sure how can I extract the
array from the constructor.
public static void main(String[] args) {
dynamicArray<Integer>array = new<Integer>dynamicArray();
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
int [] arr = array.populate();
mergeSort(arr);
System.out.println(array.toString());
}
Unfortunately, arrays and generics don't work well together. Take a look at the source code of java's ArrayList - it is implemented with an Object[] and not a T[] - then every method will cast to T (which costs literally zero, it's just ugly and causes compiler warnings). I advise you do the same here: Arrays actually KNOW their component type (unlike a list of Ts, which does not, there is no method on a java.util.List that you can invoke to get the component type), and therefore casting Object[] to T[] is just wrong; java allows this solely for backwards compatibility reasons.
Basically, you can't work with T[] without things being subtly wrong and a lot of compiler errors.
In this specific case? I would strenuously advise you to use a private List<T> array; field instead of a T[] field.
Your call to array.populate() (that seems like a bizarre name for this method!) IS retrieving the array you created in the constructor. You are doing what you're asking for: "Extracting the array from the constructor" - invoking populate() on the object returned by the new dynamicArray<Integer>() is doing exactly that.
NB: You have a typo in your source code. it's new dynamicArray<Integer>();, not new<Integer>dynamicArray();. Perhaps that's causing some issues?
NB2: Java conventions dictate it's DynamicArray, and something like getBackingArray (instead of populate).
I think you ask two question :
How to set Integer type of that array object.
How to get Integer[] to int[]
Here is the code :
private int index;
private T[] array;
public dynamicArray() {
array = (T[])new Object[10];
this.index = 0;
}
public T [] populate() {
return this.array;
}
public void add(T x) {
array[++index] = x;
}
public static void main(String[] args) {
dynamicArray<Integer>array = new<Integer>dynamicArray();
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
int[] arr = Arrays.stream(array.populate())
.mapToInt(i -> i)
.toArray();
System.out.println(array.toString());
}
Answer for 1st question is you can not set Integer type because there wasn't any add method in your class. Answer for 2nd question is you try to convert Integer[] to int[] but there is no direct way to cast this. you just need to change Integer -> Object then Object -> int. This can be done easily using streams which is in Java 8 and i have used lambda here for showing power of lambda function.
Here is a possible alternative. Pass the type of array to the constructor. But essentially you are creating a limited form of ArrayList so you may just as well use that. Note that this still has the limitation that you can't use primitive arrays as the array type.
dynamicArray<Integer> array = new dynamicArray<>(new Integer[0]);
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
Integer[] a = array.getArray();
System.out.println(Arrays.toString(a));
}
class dynamicArray<T> {
private int size = 0;
private T[] array;
public dynamicArray(T[] a) {
array = a;
}
public void add(T value) {
if (array.length == size) {
array = Arrays.copyOf(array, size == 0 ? 10 : size*2);
}
array[size++] = value;
}
#SuppressWarnings("unchecked")
public T[] getArray() {
// need to copy the array since the length and size could be different.
T[] arrayCopy = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
System.arraycopy(array, 0, arrayCopy, 0, size);
return arrayCopy;
}
}

Overload method with generic method and parameter

I've been looking through countless pages trying to figure out if it is possible to overload a method with a List parameter, with another method that has the generic return parameter List<E>. I understand that this should always try to be avoided, but it's for an assignment, and I would like to keep both methods included if it is actually possible. Below is the code for the two methods:
// This method returns an array of the info.
Object[] toArray(List data) {
Object[] array = new Object[data.size()];
for (int i = 0; i < data.size(); i++) {
array[i] = data.get(i);
}
return array;
}
// Overloaded version that uses Generics.
E[] toArray(List<E> data) {
return toArray(data);
}
My question is, besides changing the name, is there any way to differentiate the parameters that would allow both methods to be in place, and that would not cause a name clash error? Any help is appreciated. Thank you in advance!
Note: someone asked the question "How to overload a method with generic parameter in java?" on StackOverflow, which was close to what I needed, but not quite.
You don't need to overload the method because Java does this implicitly for you with raw types.
<E> E[] toArray(List<E> data) {
If you call
List objs = ...
Object[] array = toArray(objs);
and if you use generics you get generics
List<String> strs =
String[] array = toArray(objs);
The real problem you have is there is no way to implement toArray. This is because the generic type is not known at at runtime. i.e. you can't create an array of E nor can you cast an Object[] to a String[] for example.
What you can do is pass the type of the array to use.
public static Object[] toArray(List list) {
return toArray(list, Object.class);
}
public static <E> E[] toArray(List<? extends E> list, Class<E> eClass) {
#SuppressWarnings("unchecked")
E[] array = (E[]) Array.newInstance(eClass, list.size());
for (int i = 0; i < array.length;i++)
array[i] = list.get(i);
return array;
}

Collection to typed array

If I have a Collection defined as Collection collection = new ArrayList() which contains String instances, how can I convert it to a String[]? collection.toArray() returns an Object[]. Alternatively, how can I instantiate an ArrayList<String> using reflection?
Note that I cannot hardcode String, the method doing this only knows about the Class that it can work with.
Example:
Object test(Class classToCastTo, Object[] values) {
Collection collection = new ArrayList();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray();
}
If I call this with test(String.class, ...), then it will return an Object[]. How can I make it return a String[]?
Use theCollection.toArray((T[])java.lang.reflect.Array.newInstance(theClass, theCollection.size())), where T is the element type. The cast is safe as long as T is an unparameterized type.
If you have the class, you can write a method like this:
public static <T> T[] arrayBuilder(Class<T> classToCastTo, Collection c) {
return (T[]) c.toArray((T[]) Array.newInstance(classToCastTo, 0));
}
Iterate over the Collection and store it in a String array.
Try this example from my code:
Collection c = new ArrayList();
c.add("Vivek");
c.add("Vishal");
String[] arr = new String[ c.size()];
int j = 0;
for (Object s : c){
arr[j] = (String)s;
j++;
}
The following method is what are you looking for
public <T> T[] test(Class<T> classToCastTo, Object[] values) {
Collection<T> collection = new ArrayList<T>();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray((T[])Array.newInstance(classToCastTo, collection.size()));
}
Based on Ben’s answer, the following code snippet works (compiles without warnings and runs):
private static Object test(Class<?> classToCastTo, Object[] values) {
Collection<Object> collection = new ArrayList<Object>();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray(
(Object[]) java.lang.reflect.Array.newInstance(
classToCastTo, collection.size())
);
}
Now you can call the method via
String[] result = (String[]) test(String.class, someValues);
The trick is casting the array that was created via reflection to Object[] so that it satisfies the static type check and matches the parameter type required by toArray.
That said, I don’t understand why you can’t call the method with a generic parameter. If you don’t have a generic type somewhere, the result of this method will be useless anyway.
This seems to do what you need:
public static void main(String[] args) {
Object[] originalArray = {"abc", "def"};
Class clazz = String.class;
Object[] newArray = test(clazz, originalArray);
System.out.println(newArray.getClass()); //class [Ljava.lang.String;
System.out.println(Arrays.toString(newArray)); //[abc, def]
}
static Object[] test(Class classToCastTo, Object[] values) {
Object[] o = (Object[]) Array.newInstance(classToCastTo, values.length);
System.arraycopy(values, 0, o, 0, values.length);
return o;
}
You will get a java.lang.ArrayStoreException if the original array contains something that is not a String.
If you know your collection only contains Strings, this method
public static <T> T[] toArray(Collection collection, Class<T> clazz) {
T[] array = (T[]) Array.newInstance(clazz, collection.size());
return ((Collection<T>) collection).toArray(array);
}
called as
String[] result = toArray(collection, String.class);
will do what you need, though it will give some warnings about unchecked casts.
If you know your collection can only contain strings though, you ought to be able to declare it as a Collection<String> and avoid this sort of mess.

How to add new element into an array [duplicate]

This question already has answers here:
How to add new elements to an array?
(19 answers)
Closed 8 years ago.
I need to port code from blackberry to android and facing small problem:
Example: the bb code is:
public class MyClass{
private MyObject[] _myObject;
public void addElement(MyObject o){
if (_myObject == null){
_myObject = new MyObject[0];
}
Arrays.add(_myObject, o);
}
}
unfortunately android does not have Arrays.add() which is part of net.rim.device.api.util.Arrays (static void add(Object[] array, Object object))
Is there any replacement for android to dynamically extend and append in to simple array so I don't change the rest of my code.
I tried to write my own utility but it does not work:
public class Arrays {
public static void add(Object[] array, Object object){
ArrayList<Object> lst = new ArrayList<Object>();
for (Object o : array){
lst.add(o);
}
lst.add(object);
array = lst.toArray();
}
}
.. after I call
public void addElement(MyObject o){
if (_myObject == null){
_myObject = new MyObject[0];
}
Arrays.add(_myObject, o);
}
the _myObject still contain 0 elements.
Yes, because the _myObject reference is passed by value. You'd need to use:
public static Object[] add(Object[] array, Object object){
ArrayList<Object> lst = new ArrayList<Object>();
for (Object o : array){
lst.add(o);
}
lst.add(object);
return lst.toArray();
}
...
_myObject = Arrays.add(_myObject, o);
However, it would be better to just use an ArrayList<E> to start with...
There are two important things to understand here:
Java always uses pass-by-value
The value which is passed is either a reference (a way of getting to an object, or null) or a primitive value. That means if you change the value of the parameter, that doesn't get seen by the caller. If you change the value of something within the object the parameter value refers to, that's a different matter:
void doSomething(Person person) {
person.setName("New name"); // This will be visible to the caller
person = new Person(); // This won't
}
Arrays are fixed-size in Java
You can't "add" a value to an array. Once you've created it, the size is fixed. If you want a variable-size collection (which is a very common requirement) you should use an implementation of List<E> such as ArrayList<E>.
commons-lang has ArrayUtils.add(..)
guava has ObjectArrays.concat(..).
Here's the code of ObjectArrays.concat(object, array):
public static <T> T[] concat(#Nullable T element, T[] array) {
T[] result = newArray(array, array.length + 1);
result[0] = element;
System.arraycopy(array, 0, result, 1, array.length);
return result;
}
The apache-commons code is a bit longer.

Categories

Resources