I want to create an array of ArrayLists, similar to that in this thread: How to do an array of hashmaps?. However, Java gives the warning
"Cannot create a generic array of ArrayList<String>"
when I try to do the following
ArrayList[] arrayOfLists = new ArrayList[size];
I have sort of understood the problems and the workarounds provided.
I have my own approach which unfortunately does not fix the problem either.
I tried creating a list of ArrayLists and then used toArray().
ArrayList<ArrayList<String>> listOfLists = new ArrayList<ArrayList<String>>();
ArrayList<String>[] arrayOfLists = (ArrayList<String>[])listOfLists.toArray();
Worked fine, but got the warning :
Type safety: Unchecked cast from Object[] to ArrayList<String>[]
When I tried to check for type safety, using
if(listOfLists.toArray() instanceof ArrayList<String>[])
I get the error:
Cannot perform instanceof check against parameterized type ArrayList<String>[]. Use the form ArrayList<?>[] instead since further generic type information will be erased at runtime
Why cant I use this method? Why does toArray() return Object[] instead of ArrayList<String> since the instance was initialised with theArrayList<String>; type?
Any other workarounds/suggestions on how I can get this done? A 2D array will not work since different lists can vary greatly in size.
The currently accepted answer has a major error in describing Java's generics, so I felt I should answer to make sure there aren't any misconceptions.
Generics in Java are an entirely compile-time feature and for the most part don't exist at runtime due to erasure (you can get the runtime to cough up generic type information in some cases, but that's far from the general case). This provides the basis for the answers to your questions.
Why cant I use this method?
Because generics are erased, an ArrayList<String>[] (as well as all other parameterized ArrayList<>[] instances) at runtime is really an ArrayList[]. Thus, it is impossible for the runtime to check if something is instanceof ArrayList<String>[], as the runtime doesn't actually know that String is your type parameter -- it just sees ArrayList[].
Why does toArray() return Object[] instead of ArrayList since the instance was initialised with theArrayList; type?
Again, erasure. The type parameter is erased to Object, so at runtime what you effectively have is an ArrayList<Object>. Because of this erasure, the runtime doesn't have the information necessary to return an array of the proper type; it only knows that the ArrayList holds Objects, so it returns an Object[]. This is why the toArray(T[]) overload exists -- arrays retain their type information, so an array could be used to provide the requisite type information to return an array of the right type.
Any other workarounds/suggestions on how I can get this done?
As you can see, mixing generic stuff and arrays doesn't work too well, so ideally, you wouldn't mix Lists and arrays together. Therefore, if possible, you should use List<List<String>> or something of the sort instead of List<String>[]. If you want to keep a ArrayList<String>[], though, you could do this:
#SuppressWarnings("unchecked")
ArrayList<String>[] array = new ArrayList[size];
You'll still get the unchecked type warning, but you can be reasonably sure that you won't encounter heap pollution as the only reference to the object is through array. You can also use this as the parameter to toArray():
#SuppressWarnings("unchecked")
ArrayList<String>[] temp = new ArrayList[0];
ArrayList<String>[] arrayOfLists = listOfLists.toArray(temp);
or
#SuppressWarnings("unchecked")
ArrayList<String>[] arrayOfLists = listOfLists.toArray((ArrayList<String>[]) new ArrayList[0]);
For more reading on why you can't parameterize an array, see this SO question. In short, such a thing isn't safe because arrays are covariant, while generics are invariant.
The problem is that Generics are created during runtime, but type conversions and array sizes must be checkable at compile time. The compiler cannot tell what class ArrayList<String> will be during compile time (as it will be generated later), it can only tell that it will be at least an Object, because every class in Java is at least an Object. You can do type conversion and suppress the warning and it might even work, but you run into a pitfall to accidentally confuse types somewhere and mess up your code.
Java is a type-safe language by choice to prevent you from doing one of the most recurring mistakes programmers do in their daily work: confusing variable types. So while it is possible to do the type conversion, you - as an upcoming good Java programmer - should not do that. Use the ArrayList<ArrayList<String>> if you need such a construct, and use arrays only when they are necessary.
The main reason to use arrays is speed of execution, as obviously using an object will keep the runtime busy with some overhead. The main reason to not use arrays is the fact that this overhead will allow you more flexibility in coding and reduce the amount of errors you make. So as a general advice: unless you know (as in measured and determined to be a bottleneck) that you need the speed, go with Lists. Java even does some internal optimizations beyond what you would expect to speed up Lists to a point where they come very close to the execution speed of arrays.
Related
I'm having trouble figuring out what type parameter is expected at RHS of the following
ArrayList<Pair<ParseNode,ParseNode>>[] nodes = new ArrayList[indexes.length];
Why a copy of <Pair<ParseNode,ParseNode>> is not legitimate?
Arrays of concrete paramaterized types are inherently broken. Remember arrays are covariant and the array type check is a runtime operation. At runtime all the generics have been type erased, so the Array Store check can't tell <Pair<ParseNode, ParseNode>> from <Pair<BigInteger,IOException>>.
The fundamental contract of a generic is "I, the compiler, promise that if you write code that generates no warnings, you will never get a class cast exception at runtime."
Neither can the compiler guarantee to you that it will be able to give you a compile time error if something that is not an ArrayList<Pair<ParseNode,ParseNode>> is put into that array. Nor can the runtime system guarantee you will get an ArrayStoreException (like the Language Specification says it will) if you add the wrong type, rather than a ClassCastException later when you take it back out. (The second part is really why it's actually illegal rather than just a warning, it would result in an array that doesn't obey the language specification.)
So it doesn't let you declare them that way and forces you to acknowledge the 'unsafe' warning. That way it has said "I told you I can't guarantee there will not be any class cast exceptions as a result of using this array, it's on you to make sure you only put the right things in here."
Java not supports generic arrays. Arrays are covariant, generics are not. This means that if class A extends class B then A[] is also B[]. And code
A[] a = new A[10];
B[] b = a;
is legal.
But it is not same for generics. You could not assign Foo<T> to Foo<X> even if T extends X. And so elements of Foo<T>[] can't be guaranteed type safe.
EDIT
Excuse me for just linking out, but I've found Java theory and practice: Generics gotchas article, that explains everything about arrays covariance better than I even would dream.
Don't use an array. Use another ArrayList.
ArrayList<List<Pair<ParseNode,ParseNode>>> listOfLists = new ArrayList<List<Pair<ParseNode,ParseNode>>>();
listOfLists.add(new ArrayList<<Pair<ParseNode,ParseNode>>());
I was going through lectures of Algorithms on Coursera by Robert Sedgewick.I was a bit confused when Mr.Robert pointed out that one cannot use Generics with Arrays as it is not allowed.
But ArrayList in Collection Framework uses Arrays internally and Generic datatypes are allowed.I mean to say that we can do the following:
ArrayList<Integer> list = new ArrayList<Integer>();
One hack he pointed out was this:
public class FixedCapacityStack<Item>{
private Item[] s;
private int N = 0;
public FixedCapacityStack(int capacity)
{ s = (Item[]) new Object[capacity];} //this hack
He also mentioned that this is an ugly hack and must be avoided and it also produces warning during compilation.
My Question is:
1.) How does ArrayList then internally represent various Generics Types?
2.) If (assumed) they use the hack mentioned above why it doesn't produce a warning when we compile a program with ArrayList?
3.) Is there any better way apart from that cast above?
Per the source:
1 - ArrayList stores items in an Object[], and casts the value when retrieving individual elements. There's actually an #SuppressWarnings("unchecked") where the cast happens.
2 - Two answers here - the first is that you're not (typically) compiling ArrayList, but just including it on your classpath from rt.jar in the JRE/JDK. The second is that ArrayList uses a #SuppressWarnings on its unchecked conversion from Object to the generic type.
3 - Your other alternative ("better" is quite subjective) would be to require the Class for your generic type, and use Array.newInstance(Class clazz, int capacity) to create your array, as described in this question
1.) How does ArrayList then internally represent various Generics Types?
What do you mean "internally"? Generics only exist at compile time. ArrayList has already been compiled by someone else for you and you are just using the class file. So there is no generics there.
Different Java library implementations could write the source differently, but that is of no concern to you. What it does "internally" is an internal implementation detail that a user of the class should not care about.
If you were to write your own class like FixedCapacityStack, then you could do it in different ways:
You could do the thing where s is of type Item[] as you have shown above, and you create an Object[] and cast to Item[]
Or you can make s of type Object[] and cast to type Item when you get elements out of it
Note that both approaches are the same after erasure, so both will compile to the exact same bytecode. The difference is just style at compile-time.
The advantage of the first approach over the second is that when you get elements out of it, it's already the right type, so you don't have all these ugly casts everywhere. The disadvantage of the first approach is that the initial cast from Object[] to Item[] is basically a "lie", and it will only work if you make absolutely sure not to expose s to the outside of the class (e.g. do not have a method that returns s as type Item[]); otherwise you will have class cast exceptions in unexpected places.
2.) If (assumed) they use the hack mentioned above why it doesn't produce a warning when we compile a program with ArrayList?
There would only be a warning when you actually compile this class. But not if it was already compiled and you are just using the class file. In fact, you don't usually even have the source of ArrayList.
3.) Is there any better way apart from that cast above?
Depends on what you mean by "better". I have shown the two approaches and the advantages and disadvantages of each.
I have this code
ArrayList<Integer>arr = new ArrayList<>();
arr.add(1);
arr.add(2);
List l = arr;
l.add("12");// should't this throw an runtime exception? Point1
l.add("123");
System.out.println(l.size());
ArrayList<String>arr1 =(ArrayList<String>) l;// should't this throw an runtime execptions? Point2
arr1.add("12"); //Point 3
System.out.println(arr1.size());
I was experimenting with generics code, and I am surprised to see some of the results. I have this specific questions.
I have a arraylist of integer. I assign it to a list l, which does not have any generic type. I then add a string to that list. Shouldn't this throw a runtime exception? l list is still a arraylist of integer?
I then cast l to arraylist of string? Shouldn't this also throw a runtime exception? Ain't I effectively casting arraylist of integer to ararylist of string?
And in this case point 3, I am adding a string to arr1, even though it is supposed to be arraylist of string?
I feel like all of the three questioned are related? can anyone explain to me what I am doing wrong?
Generics are enforced at compile time so that the compiler can do type checking. However, through type erasure, the information about the type is actually not used at runtime. Rather, collections all just contain Object. I believe this was originally done to retain bytecode compatibility with previous java versions which didn't have generics support.
You should, however, get some warnings about using raw types.
Generics are erased at runtime, therefore it is possible but highly frowned upon to get any types in a List through casting. This is possible because the compiled code sees it as just a List, no generics. This can cause many problems if you pass the list to any methods, and you will get a runtime error. Generally, you should get an 'unchecked' warning with things like this.
Point 1 : A raw List can contain any Object, if you want to enforce typechecking (at compile time) for the content of the List, you need to specify its content type. When you assign arr to l, you lose the ability to enforce typechecking, no Exception is thus thrown and it's what is expected.
Point 2 : casting a List to an ArrayList "might be possible" (that's why there is no compilation error) as ArrayList implements List. However, as stated in point 1, you lost the ability to enforce typechecking at compile time (assigning directly arr to arr1 won't generate a compile error) that's why you have no ClassCastException. Note that you could even assign a new LinkedList()to l then assign l to arr1and it would have compiled (but it would have raised a class cast exception at runtime as in this case the cast is detected as being impossible at runtime)
Point 3 : it is an ArrayList of Strings, you add a String, it's typesafe.
Generally, Java can be considered as a type-safe language. I know that there are some flaws with generics, but I recently came across a Problem I never had before.
To break it down:
Object[] objects = new Integer[10];
objects[0] = "Hello World";
will NOT result in a compile-time error as expected. I would assume that the declaration of an Array of Object will disallow to point to to an array of something else. In Generics I'm not allowed to make such weird things like:
ArrayList<Object> objs = new ArrayList<Integer>
and if I try to kind of trick Java into doing something with
ArrayList<? extends Object> objects = new ArrayList<Integer>
I'm allowed to declare it, but I can only add Objects of type null.
Why doesn't Java prevent the declaration of such weired arrays?
Firstly, I should point out that this is type-safe.
Object[] objects = new Integer[10];
objects[0] = "Hello World";
because an exception will be thrown. (It is not statically type-safe ... but that is a different statement entirely.)
The reason that Java allows this is historical. Until Java 5, Java did not support any form of generics. Gosling has said that if they had had the time to figure out and incorporate generics into Java 1.0, they would have done so.
Unfortunately, they didn't. But they still wanted to be able write things like a general purpose sort method with the following signature:
void sort(Object[] array, Comparator comp) ...
To make this method work for any kind of object array (without generics), it was necessary to make arrays covariant; i.e. to make it legal to pass a String[] or Integer[] as an argument where the formal type is Object[]. If they hadn't done that you would have had to copy the String[] to an Object[], sort it, and then copy it back.
I don't think there's an answer to this besides "legacy design". (Which I admit is a fancy way of saying "because".) You pretty much need to be able to do an equivalent of the last assignment you show somehow. (Otherwise you're stuck to making lots and lots of copies with manual up/down casts, assuming language features of Java pre 1.4)
In Java 1 when type semantics for arrays were basically set in stone, generics weren't available, or even up for consideration for a long while yet. So there was no mechanism available to express the higher-order type constraints needed to make this construct type-safe – and Gosling (IIRC a fan of simplicity) felt resolving this edge case of compile-time type safety wasn't worth complicated the language with whichever solutions were available. Or wasn't bothered by doing the check at runtime enough to even look for a solution. (At the end of the day language design decisions are arbitrary to at least some degree, and there's only one person that could answer this with any certainty.)
"Because it has to".
To elaborate a bit, consider the following example:
Object[] objects = null;
if (something) {
objects = new Integer[10];
} else {
objects = new String[10];
}
Now, how would the Java compiler know which assignments to allow and which to refuse? It can't. The compile-time type is Object so the compiler will let you put any Object in your array, simply because it doesn't have any knowledge of the runtime type of your array.
actually in case of arrays you get a exception at run time called ArrayStoreException when you add wrong type of element In this case a String. In case of generics there is no such exception. its for the very same reason you are not allowed to add anything but object, as you might just add a wrong type into the list.
There's Discussion that I found while I google it
I Found:
Firstly, arrays do not break type safety. If they did, then upcasting
an array wouldn't fail at runtime. They make it impossible for the
compiler to prove the program type-safe, so the checking is deferred
until runtime.
I think confusion occurs here because one the one hand, since a String
is-a Object an array of Strings is-obviously-an array of Objects, and
on the other it clearly isn't. The answer is in the mutability of an
array.
If the array is immutable, then it safe to treat a String[] as an
Object[], because an immutable String[] is always exactly like an
immutable Object[].
On the other hand, if the array is mutable, then it is not usually
safe to treat a String[] as an Object[].
The "wildcards" technique described in the above link is exactly what
CommonLisp has been doing for years.
(deftype StringArray? () (array String)) ; This is the type of arrays of String
(deftype ObjectArray? () (array Object)) ; This is the type of arrays of Object
(subtypep StringArray? ObjectArray?) ; Is StringArray? a subtype of ObjectArray?? false, true ; No, it isn't. (false: it isn't, true: I'm ure)
(deftype AnyArray? () (array *)) ; This is the type of arrays of anything (subtypep StringArray? AnyArray?) ; Is StringArray? a subtype of AnyArray?? true, true ; Yes, it is. (true: it is, true: I'm sure)
I'm refatoring a home-grown DAO container, hoping to make the class generic. It internally uses an ArrayList to store the retrieved objects.
One usage of this class puts the container's list into a request scope, and due to a limitation of Websphere, I can't pass the generic List<Foo> to the request scope (Websphere doesn't handle generics out-of-the-box)
If I go ahead with my refactorings, I will need to convert/cast the List<Foo> into a non-generic List object..
// Boils down to this...
List<Foo> listFoo = new FooListing().findAllFoo();
List listThings = listFoo;
request.setAttribute("listThings", listThings);
What are the implications of reversing a generification like this? Should I avoid doing this kind of manipulation?
EDIT: The code snippet is verbose to explicitly demonstrate what I'm describing..
If the component type of the List does match the expected type, there is no problem.
Generics in Java are only used for type-checks by the compiler, they have not effect at runtime. If you are using an older library that does not support generics, you have no choice but to ignore the generic type.
Things should continue to work, as this system has been designed with backwards compatibility in mind.
So all you are losing is the compile-time type checking (it puts you back to where Java was at 1.4, which means, if the types match, everything will work, if not, you'll get ClassCastExceptions or other unwanted behaviour at runtime).
However, I think you can just write
request.setAttribute("listThings", listFoo);
This method takes any kind of Object. Even if it wanted a List, you could still pass a List<Foo> (which is still a List).
Java uses "type erasure" for generics -- essentially that means that the compiler checks the generics, but the runtime forgets all about it and just treats it as a list of objects.*
Whenever you treat a List<Foo> as just a List, you won't get compiler checks to make sure you don't put a Bla into your list. So you could get a ClassCastException if you call List<Foo>.get() and it turns out to be a Bla hiding in the list. But that can only happen if you some code puts a Bla in your list.
If you wan't to be cautious, then if you pass the List<Foo> as a List to anything that might add a non-Foo to the list, don't treat it as a List<Foo> whenever you access it, but treat it as a list of Objects and add instanceof checks.
*Some of the information is accessible at runtime, but let's not complicate matters.
A "non-generic" version of a generic type is called a "raw type".
Passing a generic type where the raw equivalent is requested is generally ok. This is actually the main reason generics in Java work the way they do (with erasure): to enable interoperability between "generified" code and pre-generics code.
The main thing you need to be careful about is that if you pass a List<Foo> to something that askes for a List, they may put non-Foo objects into the List. You won't get any compile time checking to help you here. You do get some runtime checks: a ClassCastException will be thrown when you use a method that returns a Foo on your List<Foo> and it has to return a non-Foo.
If you want more fail-fast behavior you can wrap your List<Foo> with Collections.checkedList() to get a List that'll check the type of elements on insertion.
Things get more complicated if Foo itself is a generic type. Runtime checks are only done on reified types (ie: the type with generic type parameters removed) so if you give them a List<Set<Bar>> and they insert a Set<Baz> or just a Set, you won't know since the runtime/reified type of the element is Set either way.
First, you can't cast a generic to a non-generic list so yeah you'd have to convert it.
Second, the two main advantages to a generic list are 1) it ensures that all objects are of the specified type and 2) it allows you to directly access methods of the object collection without needing to recast them. This allows you to write cleaner code and saves some processing cycles from having to cast back and fourth.
Neither one of these advantages is a dire need however. If you can't use them you won't notice a difference in performance. Your code may look a little messier though.
I have similar problems with Weblogic Portal. Just use none-generic type for this case.