I have a HashMap:
Map stuff = new HashMap<String, ArrayList<Thing>();
I am trying to use the .get() method to get the ArrayList, but I have an error message saying,
Object cannot be converted to ArrayList;
I'm not really sure why I am getting this error message? Also, is there another way to acquire the ArrayList at the key?
Try parameterizing the Map declaration with <>.
Map<String, ArrayList<Thing>> stuff = new HashMap<>() ;
Without the type declaration it will be an untyped Map, so the compiler will only be able to assume that the results of get are the most generic possible result, an Object.
With the diamond operator the compiler will infer the type of the right hand side from the left hand declaration.
You can cast the result of get back into an ArrayList, but it is better to parameterize the Map declaration instead. A cast will require runtime checks which have some performance cost and can potentially introduce crashes once your code becomes more difficult to understand. If you use the parameterized form instead, the compiler can prove that it is the right type, preventing crashes and running faster.
If you're using an old java from before java 1.7 you'll need to write out the full type in the <>.
See this tutorial from oracle for more information.
Related
I have a line of code which compiles and works:
HashMap<String, Object>[] resultArray =
new ObjectMapper().readValue(json, HashMap[].class);
It uses Jackson ObjectMapper.
And I have another line, which gives Generic array creation error:
HashMap<String, Object>[] resultArray = new HashMap<String, Object>[] { resultObject };
I know that it is not possible to do this in Java. But obviously readValue() somehow did it. How can it be?
readValue created an array of the raw type, HashMap[] -- not of HashMap<String, Object>[]. Raw types are a relatively low-level, mostly-obsolete feature that you should try to avoid if possible; but with certain things, like reflection (which Jackson relies on heavily), it's often not possible.
The short of it is that Jackson is playing with a dangerous tool. As with many dangerous tools, it's not that it'll always cause damage; it's that it can cause damage, and should thus be used carefully and with some amount of experience.
The danger essentially boils down to the fact that with raw types, the compiler can't track the actual, parameterized types you want to work with; and that means it can't protect you from using them incorrectly.
At some point, you'll probably be casting this raw array to a generic one; something like:
HashMap[] original = new HashMap[0];
HashMap<String,Object>[] publiclyVisible = original;
// or
#SuppressWarnings("unchecked")
HashMap<String,Object> singleElement = original[0];
Because of erasure, there's actually nothing in the JVM that can track that publiclyVisible is meant to hold an array of HashMap<String, Object>s. All the JVM knows is that it holds an array of raw HashMaps. Thus, if you put a Map<Integer, Foo> into original, the JVM will let you; and if someone later retrieves it as a Map<String, Object>, the JVM will happily allow that, too. It's only when someone tries to use the types that they'll get into trouble, and it'll be at a very non-obvious way. For instance, maybe they'll do:
Map<String, Object> myMap = publiclyVisible[0];
for (String key : myMap.keySet()) {
...
... and they'll get a ClassCastException on that for line, saying that Integer can't be cast to String. This is very confusing! The problem is that the JVM has erased the type information, such that it couldn't track any of the improper array stuff. The first time it detects something is amiss is when it casts the reference of the first myMap key — which is an Integer, because this was accidentally a HashMap<Integer, Foo> — to a String. Essentially, the programmer expects myMap's keys to all be Strings, but erasure and raw types have conspired to allow them to be any type at all.
There are other variants of the problem, and other confusing ways to trigger it; but they all boil down to the same basic problem. In short, erasure means that the JVM can't track the actual types of your references, and raw types means the compiler can't track them either, and when you combine those them you can increase the likelihood of a confusing bug.
In the Java implementation, I found
transient Entry[] table;
which is initiated in constructor as
table = new Entry[capacity];
I know and understand that creating generic array is not allowed but then what I fail to understand is that how the whole thing works. I mean when we do something like
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
How does above codes leads to creating an Entry array of type <Integer, String>
Well, few people are not able to understand what I am asking. To rephrase what I am asking is what is the point in doing something like
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
When it does not result in
Entry<Integer, String>
Generics are a compile-time safety. At runtime, the map only know about Objects. This is known as type erasure. To scare you even more, the following code will run without problem:
Map<Integer, Integer> safeMap = new HashMap<>();
Map unsafeMap = safeMap;
unsafeMap.put("hello", "world");
You'll get a warning at compile time, because you're using a raw Map instead of a generic one, but at runtime, no check is done at all, because the map is a good old map able of storing any object. Only the compiler prevents you from adding Strings in a map or integers.
The implementation makes an array of Entry<K,V> objects of type
static class Entry<K,V> implements Map.Entry<K,V>
without providing generic type parameters (source). This is allowed, but it comes with understanding that the compiler is no longer guarantees type safety. For example, in other places in code you could write
Entry<K,V> e = table[bucketIndex];
and the compiler will let you do that. If you know for sure that you always set elements of table[] to null or Entry<K,V>, then you know that the assignment is correct.
The reason this works without a problem is that generic types in Java are implemented through type erasure, i.e. there is no difference at runtime between Entry<K,V> objects Entry<Integer,Integer> and Entry<String,Long>.
Try to think of Java Generics this way: type parameters only apply to the static type of reference-typed expressions and do not apply to the type of actual instances being referred to by the reference values at runtime.
I find the above key to developing the proper intuitions when reading Java code. So the next time you see
new HashMap<Integer, String>()
read it as follows: "This is an instance creation expression of the type HashMap<Integer, String>. At runtime this expression will yield a reference to an instance of the HashMap class." As long as the compiler can precisely track what you do with the result of that expression, it can maintain the knowledge that this is indeed a HashMap<Integer, String>, but no further than that.
Now, since the static type system is not powerful enough to track the type parameters on the component type of arrays (the fact that Java's array types are covariant plays strongly here), the code is forced to break out of the static type safety network. The key observation is that on its own, this does not make the code incorrect, it only constrains the power of the compiler to find programming mistakes. This is why Java allows you to make unchecked casts from raw into generic types, although not without a warning which marks the spot where you have left the provinces of static type safety.
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.
My Java application is displaying some odd behaviour, and I'm having trouble finding a solution.
This code snippet takes the contents of a LinkedHashMap where the keys and values are both of type String and writes them to a text file. configWriter is of type PrintWriter.
for (Map.Entry<String, String> entry : configMap.entireSet()) {
configWriter.println(entry.getKey() + "=" + entry.getValue());
}
The map was declared:
LinkedHashMap<String, String> configMap = new LinkedHashMap<String, String>();
It is populated by reading tokens from a text file using the Scanner class.
One of the values in the map is a String that contains only numbers (an integer), but when the loop gets to this value, it throws a ClassCastException saying that you cannot cast from Integer to String. This makes perfect sense, but the type of the values is String.
Is there a way to force Java to keep this value as a String?
It's almost certain that rawtypes are getting used somewhere and there's an Integer sneaking its way into the map.
The easiest way to identify where an Integer is getting inserted into the map is to replace the initialization of configMap with
Map<String, String> configMap = Collections.checkedMap(
new LinkedHashMap<String, String>(), String.class, String.class);
which will actually throw an exception if something other than a String gets inserted into the map. I wouldn't personally use that in production code, but this is probably the simplest way to "smoke out" the bug; you'll get an exception and a stack trace for where exactly the Integer is getting inserted.
As it stands, the way generics are implemented with type erasure means that there's nothing actually stopping non-String values from being inserted into the map. The compiler makes some effort to prevent it, but rawtypes and legacy code can get around it. Using Collections.checkedMap will force the issue, though.
Louis Wasserman has explained the probable cause of your problems.
I just want to point out that there are a couple of flaws in your thinking in your question.
Why is Java automatically casting from String to Integer when reading LinkedHashMap
First, you can't cast from a String to an Integer. With Java reference types, you can only cast an object to a type if the object is an instance of that type, or a subtype of that type. The String type is not a subtype of Integer, so the operational semantics of the JVM forbid such a cast - you'd get a runtime exception if you wrote compilable code that tried to do this.
Second, there is nothing in LinkedHashMap ... or any other collections class which would convert an element/key/value instance from String to Integer without you telling it to. Well-behaved (Java) APIs just don't do that kind of thing.
Which brings us back to Louis's diagnosis - something (e.g. your code) has added that Integer object as a value. It is the kind of thing that can happen if you use raw types or suppress / ignore "unchecked conversion" warnings.
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.