Creating a Generic array without using Arraylist - java

In order to complete one of my Java assignments, I have to do what seems like the impossible.
I have to create a method that takes in different stuff and plugs it into an array. We don't necessarily know what is being put into the array and thus the array must be able to accept Strings, Double, Integer, etc...
Of course, the obvious solution would be to use ArrayList<E> (i.e. a generic array). However, that's partly the complication of the problem. We cannot use an ArrayList, only a regular array. As far as I can find, when creating an array its intake value must be declared. Which leads me to believe that this assignment is impossible (yet I doubt the teacher would give me an impossible assignment).
Any suggestions?

You can always use an array of Object - Object[].
Object[] objects = new Object[2];
objects[0] = "ABC";
objects[1] = Integer.valueOf("15");

Are you sure you need a generic array or an array that can hold anything?
If the former, then create a class that will act as wrapper of Object[] array and use a <T> generic for type cast when getting the elements of the array, which is similar to the implementation of ArrayList class. If the latter, use Object[] directly.

Related

Could java.util.ArrayList<T>.toArray() be made friendlier?

I'm surprised by how painful it is to use java.util.ArrayList<T>.toArray().
Suppose I declare my array list as:
java.util.ArrayList<double[]> arrayList = new java.util.ArrayList<double[]>();
... add some items ...
Then to convert it to an array, I have to do one of the following:
double[][] array = (double[][])arrayList.toArray(new double[0][]);
or:
double[][] array = (double[][])arrayList.toArray(new double[arrayList.size()][]);
or:
double[][] array = new double[arrayList.size()];
arrayList.toArray(array);
None of the above are very readable. Shouldn't I be able to say the following instead?
double[][] array = arrayList.toArray();
But that gives a compile error because Object[] can't be converted to double[][].
Perhaps it's not possible because toArray has to return Object[]
for backwards compatibility with pre-template days.
But if that's the case, couldn't a friendlier alternative method be added
with a different name? I can't think of a good name, but almost anything
would be better than the existing ways; e.g. the following would be fine:
double[][] array = arrayList.toArrayOfNaturalType();
No such member function exists, but maybe it's possible to write a generic helper function that will do it?
double[][] array = MyToArray(arrayList);
The signature of MyToArray would be something like:
public static <T> T[] MyToArray(java.util.ArrayList<T> arrayList)
Is it possible to implement such a function?
My various attempts at implementing it resulted in compile errors
"error: generic array creation" or "error: cannot select from a type variable".
Here's the closest I was able to get:
public static <T> T[] MyToArray(java.util.ArrayList<T> arrayList, Class type)
{
T[] array = (T[])java.lang.reflect.Array.newInstance(type, arrayList.size());
arrayList.toArray(array);
return array;
}
It's called like this:
double[][] array = MyToArray(arrayList, double[].class);
I wish the redundant final parameter wasn't there, but, even so,
I think this is the least-horrible way that I've seen so far for converting array list to array.
Is it possible to do any better than this?
Is it possible to do any better than this?
Nope.
None of the above are very readable. Shouldn't I be able to say the following instead?
double[][] array = arrayList.toArray();
It would be nice ... but you can't.
The problem is that the toArray() method was specified way back in Java 1.2 with the behavior you are seeing. Generic types were not added to the language until Java 1.5. When they were added, the designers chose the "type erasure" approach, for compatibility with earlier versions of Java. So:
the semantics of the toArray() methods could not be changed without breaking compatibility, and
type erasure makes it impossible for a toArray() method implementation to know what the list's actual element type is, so it could not get it right anyway.
Unfortunately you cannot write
double[][] array = arrayList.toArray();
The reason is that toArray() was defined in JDK 1.2 (prior to generics) to return Object[]. This cannot be changed compatibly.
Generics were introduced in Java 5 but were implemented using erasure. This means that the ArrayList instance has no knowledge at runtime of the types of objects it contains; therefore, it cannot create an array of the desired element type. That's why you have to pass a type token of some sort -- in this case an actual array instance -- to tell ArrayList the type of the array to create.
You should be able to write
double[][] array = arrayList.toArray(new double[0][]);
without a cast. The one-arg overload of toArray() is generified, so you'll get the right return type.
One might think that it's preferable to pass a pre-sized array instead of a throwaway zero-length array. Aleksey Shipilev wrote an article analyzing this question. The answer is, somewhat counterintuitively, that creating a zero-length array is potentially faster.
Briefly, the reason is that allocation is cheap, a zero-length array is small, and it's probably going to be thrown away and garbage collected quickly, which is also cheap. By contrast, creating a pre-sized array requires it to be allocated and then filled with nulls/zeroes. It's then passed to toArray(), which then fills it with values from the list. Thus, every array element is typically written twice. By passing a zero-length array to toArray(), this allows the array allocation to occur in the same code as the array filling code, providing the opportunity for the JIT compiler to bypass the initial zero-fill, since it knows that every array element will be filled.
There is also JDK-8060192 which proposes to add the following:
<A> A[] Collection.toArray(IntFunction<A[]> generator)
This lets you pass a lambda expression that is given the array size and returns a created array of that size. (This is similar to Stream.toArray().) For example,
// NOT YET IMPLEMENTED
double[][] array = arrayList.toArray(n -> new double[n][]);
double[][] array = arrayList.toArray(double[][]::new);
This isn't implemented yet, but I'm still hopeful this can get into JDK 9.
You could rewrite your helper function along these lines:
static <T> T[] myToArray(List<T> list, IntFunction<T[]> generator) {
return list.toArray(generator.apply(list.size()));
}
(Note that there is some subtlety here with concurrent modification of the list, which I'm ignoring for this example.) This would let you write:
double[][] array = myToArray(arrayList, double[][]::new);
which isn't terribly bad. But it's not actually clear that it's any better than just allocating a zero-length array to pass to toArray().
Finally, one might ask why toArray() takes an actual array instance instead of a Class object to denote the desired element type. Joshua Bloch (creator of the Java collections framework) said in comments on JDK-5072831 that this is feasible but that he's not sure it's a good idea, though he could live with it.
There an additional use case here as well, of copying the elements into an existing array, like the old Vector.copyInto() method. The array-bearing toArray(T[]) method also supports this use case. In fact, it's better than Vector.copyInto() because the latter cannot be used safely in the presence of concurrent modification, if the collection's size changes. The auto-sizing behavior of toArray(T[]) handles this, and it also handles the case of creating an array of the caller's desired type as described above. Thus, while adding an overload that takes a Class object would certainly work, it doesn't add much over the existing API.

Adding values to Arraylist

Code 1:
ArrayList arr = new ArrayList();
arr.add(3);
arr.add("ss");
Code 2:
ArrayList<Object> arr = new ArrayList<Object>();
arr.add(3);
arr.add("ss");
Code 3:
ArrayList<Object> arr = new ArrayList<Object>();
arr.add(new Integer(3));
arr.add(new String("ss"));
all the above three codes are working fine.. can some one tell me the which is prefered and why.. and why the eclipse compiler always gives warning when type of arguments are not mentioned to the Arraylist.. thanks in advance..
First simple rule: never use the String(String) constructor, it is absolutely useless (*).
So arr.add("ss") is just fine.
With 3 it's slightly different: 3 is an int literal, which is not an object. Only objects can be put into a List. So the int will need to be converted into an Integer object. In most cases that will be done automagically for you (that process is called autoboxing). It effectively does the same thing as Integer.valueOf(3) which can (and will) avoid creating a new Integer instance in some cases.
So actually writing arr.add(3) is usually a better idea than using arr.add(new Integer(3)), because it can avoid creating a new Integer object and instead reuse and existing one.
Disclaimer: I am focusing on the difference between the second and third code blocks here and pretty much ignoring the generics part. For more information on the generics, please check out the other answers.
(*) there are some obscure corner cases where it is useful, but once you approach those you'll know never to take absolute statements as absolutes ;-)
The second one would be preferred:
it avoids unnecessary/inefficient constructor calls
it makes you specify the element type for the list (if that is missing, you get a warning)
However, having two different types of object in the same list has a bit of a bad design smell. We need more context to speak on that.
The second form is preferred:
ArrayList<Object> arr = new ArrayList<Object>();
arr.add(3);
arr.add("ss");
Always specify generic arguments when using generic types (such as ArrayList<T>). This rules out the first form.
As to the last form, it is more verbose and does extra work for no benefit.
Actually, a third is preferred:
ArrayList<Object> array = new ArrayList<Object>();
array.add(Integer.valueOf(3));
array.add("ss");
This avoids autoboxing (Integer.valueOf(3) versus 3) and doesn't create an unnecessary String object.
Eclipse complains when you don't use type arguments with a generic type like ArrayList, because you are using something called a raw type, which is discouraged. If a class is generic (that is, it has type parameters), then you should always use type arguments with that class.
Autoboxing, on the other hand, is a personal preference. Some people are okay with it, and some not. I don't like it, and I turn on the warning for autoboxing/autounboxing.
You are getting the warning because ArrayList is part of java generics. Essentially, it's a way to catch your type errors at compile time. For example, if you declare your array list with types Integer (ArrrayList<Integer>) and then try to add Strings to it, you'll get an error at compile time - avoiding nasty crashes at runtime.
The first syntax is there for backward compatibility and should be avoided whenever possible (note that generics were not there in older versions of java).
Second and third examples are pretty much equivalent. As you need to pass an object and not a primitive type to add method, your 3 is internally converted to Integer(3). By writing a string in double-quotes you effectively are creating a String object. When calling String("ss") you are creating a new String object with value being the same as the parameter ("ss").
Unless you really do need to store different types in your List, I would suggest actually using a proper type declaration, e.g. ArrayList<Integer> = new ArrayList<Integer>() - it'll save you a lot of headache in the long run.
If you do need multiple datatypes in the list, then the second example is better.
Two last variants are the same, int is wrapped to Integer automatically where you need an Object. If you not write any class in <> it will be Object by default. So there is no difference, but it will be better to understanding if you write Object.
Well by doing the above you open yourself to run time errors, unless you are happy to accept that your arraylists can contains both strings and integers and elephants.
Eclipse returns an error because it does not want you to be unaware of the fact that by specifying no type for the generic parameter you are opening yourself up for run time errors. At least with the other two examples you know that you can have objects in your Arraylist and since Inetegers and Strings are both objects Eclipse doesn't warn you.
Either code 2 or 3 are ok. But if you know you will have either only ints or only strings in your arraylist then I would do
ArrayList<Integer> arr = new ArrayList<Integer>();
or
ArrayList<String> arr = new ArrayList<String>();
respectively.
There's a faster and easy way in Java 9 without involving much of code: Using Collection Factory methods:
List<String> list = List.of("first", "second", "third");
in the first you don't define the type that will be held and linked within your arraylist construct
this is the preferred method to do so, you define the type of list and the ide will handle the rest
in the third one you will better just define List for shorter code

Resolving ClassCastException in this code

I have this method getData as shown .
It is expecting an array of bag Objects as shown
please see the code below :
public static String getData(Bag[] bag)
{
}
public class Bag
{
public char side;
}
But , when i tried i am getting ClassCastException .
I have done this way :
Object bagArray[] = new Object[1];
Bag bagData = new Bag();
bagData.side = 'S';
bagArray[0]=bagData;
String bagData = ApplicationUtil.getData(Bag[]) bagArray);
Please let me , how to resolve this error ??
Why are you creating an Object array rather than an array of Bag objects?
Try just changing the first line to Bag[] bagArray = new Bag[1].
As an Object array can hold any kind of object, so I don't think it can be cast to a Bag array. You could however cast bagArray[0] to a Bag object.
In future, try using a List or other collection rather than an array for stuff like this.
The problem is that bagArray is an array of Object and not an array of Bag.
Either change that to Bag bagArray[] = new Bag [1]; or use a Collection (e.g. List) instead - note that you can cast List<Object> to List<Bag> but that is an unsafe operation and not recommended unless you know what you're doing.
You're trying to cast an Object[] into a Bag[]. Not allowed.
You bagArray is an Object array and not a Bag array. Just because it is capable of holding an object of type Bag (which is a subclass of Object), does not mean the vice versa. You are trying to cast Object to Bag type, which is not allowed. Define you bag array in the following way
Object bagArray[] = new Bag[];
See this question: Quick Java question: Casting an array of Objects into an array of my intended class
As others have said, you can't cast an Object[] to, well, anything. In this case, you have a Bag inside an Object array, so in this specific instance it seems like it might work. But imagine that you had a larger array, full of objects of different types. In that case, the cast wouldn't work. The program has to work for the general case.
You can solve this by:
1) Using a Bag[] type instead of Object[]
2) Using a List - collections are nearly always better
3) Using the Arrays class to create a new Bag[]: Arrays.copyOf(bagArray,bagArray.length,Bag[].class)

Java array of Hashtables

I need an array of Hashtables in a program that is storing all words from a given set of documents.
Index 1 of the array holds a hashtable of String -> Double which stores a word, and its count for document 1 (array index 100 = document number 100's hashtable).
I dont need help using this data structure, just in creating it.
I declare the Hashtable Array as follows:
Hashtable<String,Double>[] h1 = new Hashtable<String,Double>[];
... but this does not compile.
(NOTE: The Double is necessary rather than an Integer in the above declaration for later usage.)
QUESTION:
How do you create an array of hashtables which stores String->Double ???
Any suggestions appreciated guys....
... but this does not compile.
That's because the array has no name, new expects a number of elements and you can't just allocate an array of generics. Prefer a List instead:
List<Hashtable<String,Double>> wordCountPerDoc
= new ArrayList<Hashtable<String,Double>>();
just use
#SuppressWarnings("unchecked")
Hashtable<String,Double>[] h = (Hashtable<String,Double>[])new Hashtable<?,?>[10];
h[0] = new Hashtable<String, Double>();
why don't you use a Map<Integer, Map<String, Double> > ?
this way you don't waste space for non-existing documents, and still get O(1) retrieval.
you can create like this.
Hashtable<String,Double>[] arr = new Hashtable[10];
Two things: you can't declare an array with the parameterized types like that; you have to imply declare it a new Hashtable[]. And you need to give the array a length.
Mixing arrays and Collections, although possible, tends to be confusing and lead to problems in my experience; also HashMap is generally preferred to Hashtable. So I would tend to prefer a List<Map<String, Double>> for this application.
The reasons why this is an error are covered in Angelika Langer's Generics FAQ: Can I create an array whose component type is a concrete parameterized type?
Can I create an array whose component type is a concrete parameterized type?
No, because it is not type-safe.
Arrays are covariant, which means that
an array of supertype references is a
supertype of an array of subtype
references. That is, Object[] is a
supertype of String[] and a string
array can be accessed through a
reference variable of type Object[].
Arrays and generics can have odd interactions (largely due to implementation compromises to support compatibility). You may be better off (as larsmans suggested) looking at a suitable collection type such as a List of Maps.
An array seems to be an unusual choice of structure here. Perhaps you should consider storing your hashtables in a List. It will dynamically resize for you if you don't know how many document you will have ahead of time. If you use an ArrayList, you will still have constant-time reads of random indeces (like an array.) I think it's much simpler than using an array, and you still get the generic type checking. If you choose a List, you syntax becomes:
List<Map<String,Double>> documentWordCounts = new ArrayList<Map<String,Double>>();
Or choose a LinkedList depending on what kind of read/write pattern you want.
For fixed size array:
Hashtable<String,Double>[] h1 = new Hashtable[]{new Hashtable< String,Double>(),new Hashtable< String,Double>(),new Hashtable< String,Double>()};

java: how can I create a dynamic array without forcing the type of the values?

I need to create a dynamic array in Java, but the values type differ from String to Int to float. how can I create a dynamic list that I don't need to give it in advanced the type of the values?
The keys just need to be ascending numbers (1,2,3,4 or 0,1,2,3,4)
I checked ArrayList but it seems that I have to give it a fixed type for the values.
thanks!
You can have an array or an ArrayList of Objects which will allow you to contain String, int, and float elements.
You can use this:
List<Object> myList = new ArrayList<Object>();
Integer i = 1;
Double d = 1.2;
String s = "Hello World";
myList.add(i);
myList.add(d);
myList.add(s);
It's pretty rare, in my experience, to want a List<Object>. I think it might be a design smell, and I'd examine the design to see if another set of structures might better represent your data. Without knowing anything about what you're trying to solve, it's hard to say with any confidence, but typically one wants to do things with what one has put into a list, and to do anything meaningful with things once they're just Object, you'll need to examine their type and get reflective, to kind of break away from language basics. Versus storing them in more type-sensitive structures, where you can deal directly with them in their original types without reflection magic.
It's more trouble than it's worth, but it is possible to interact with arrays reflectively.
import java.lang.reflect.Array;
// ...
Object arr = Array.newInstance(int.class, 10);
System.out.println(arr.getClass().getName()); // prints "[I"
System.out.println(Array.getLength(arr)); // prints "10"
Array.set(arr, 5, 42);
if (arr instanceof int[]) {
int[] nums = (int[]) arr;
System.out.println(nums[5]); // prints "42"
}
References
java.lang.reflect.Array API
Do note that in the API you pass arrays as Object. This is because Object is the superclass of all array types, be it int[].class or String[][].class. This also means that there is little compile time safety (as is true with reflection in general). Array.getLength("mamamia") compiles just fine; it'll throw an IllegalArgumentException at runtime.

Categories

Resources