Generics in HashMap dont seem to be used consistently - java

Firstly new to Generics. Now question - In the HashMap.java I see the following -
transient Entry[] table;
which is initiated in constructor as
table = new Entry[capacity];
Why was this not declared with type parameters ?
Or
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
Why was Entry in the for loop declared with type parameters?
Is there a deep concept or just an affordable inconsistency ?

That's because creating an array of concrete parameterized type is not type safe, and that is why that's not allowed at all.
If you try code something like this, you would get a compiler error:
List<String>[] arr = new ArrayList<String>[10]; // Compiler error: Generic Array creation
The issue is that generic types are non-reifiable - their type information is not available at runtime. Meanwhile, arrays use type information that is available at runtime to do an ArrayStoreCheck to see if an element inserted into an array is compatible with the array's type. So, if you mix up arrays and generics, then you might end up having surprising behaviour at runtime.
For example, consider the following code:
List<String>[] arr = new ArrayList<String>[10]; // Suppose this was valid
Object[] objArr = arr; // This is valid assignment. A `List[]` is an `Object[]`
objArr[0] = new ArrayList<Integer>(); // There you go. A disaster waiting at runtime.
String str = arr[0].get(0); // Assigned an `Integer` to a `String`. ClassCastException
So, had the 1st assignment compiled, the 4th assignment, which looks fine to the compiler, would have throw a ClassCastException at runtime.
However, you can create an array of raw types - ArrayList, or unbounded wildcard parameterized type - ArrayList<?>, as both of them are fully reifiable types. So the following array creations are valid:
List[] arr = new ArrayList[10];
List<?>[] arr2 = new ArrayList<?>[10];
Since there is no type information associated with raw types or unbounded wildcard types, there is nothing to lose at runtime. And hence those types are reifiable, they are an eligible component type of an array. That is why Entry[] is used instead of Entry<K, V>[].
See also:
How to create a generic array?

Related

Unchecked or Unsafe Operation error with Array of ArrayLists

When I declare the Array of ArrayLists below, it compiles correctly but has a warning stating, "Unchecked or Unsafe operations." Node is an inner class that I created within my public class.
private ArrayList<node>[] arr = new ArrayList[5];
I already tried both of the following, neither of which compiled:
private ArrayList<node>[] arr = new ArrayList<node>[5];
private ArrayList<node>[] arr = new ArrayList<>[5];
What is the issue here? Thank you for any help.
Arrays know their component type at runtime and every time you set a non-null reference on an array, it checks at runtime, that the object the reference points to is an instance of the array's component type; if it isn't, it's guaranteed to throw an ArrayStoreException.
Object[] foo = new Integer[42];
foo[0] = "bar"; // throws ArrayStoreException -- "bar" is not an instance of Integer
However, it's not possible to check that an object is an instance of a parameterized type, since objects do not know their type arguments at runtime. It's only possible to check that an object is an instance of a reified type at runtime; and that's why array component types are only allowed to be reified types.
You can still create an array of the raw type and assign it to the type of array of the parameterized type, as you are doing:
private ArrayList<node>[] arr = new ArrayList[5];
Or you can create an array of the unbounded wildcard type (which is also reified) and then explicitly cast it to the type of array of the parameterized type:
private ArrayList<node>[] arr = (ArrayList<node>[])new ArrayList<?>[5];
In both cases there will be an unchecked warning. By giving you a warning, that means you take responsibility for bad things that might happen. For example:
Object[] foo = arr;
foo[0] = new ArrayList<String>(); // no ArrayStoreException -- it is an instance of ArrayList
// now you have an ArrayList<node>[] that contains an ArrayList<String>
Generic types are non-refied. That is they are fictitious and not available at run time. For an instance ArrayList<String> becomes ArrayList at run time. All the generic information is erased at the time you compile the code.
On the other hand arrays are refied, meaning they have more information retained at run time. So in run time all you have is new ArrayList[5]. That's why compiler does not allow this.
These operations are illegal because they are not type safe.
private ArrayList<node>[] arr = new ArrayList<node>[5];
private ArrayList<node>[] arr = new ArrayList<>[5];
If it were legal, casts generated by the compiler could fail at runtime. This will violate the fundamental guarantee provided by the generic type system.

How to safely convert from a Collection of generic types to an array? [duplicate]

This question already has answers here:
How to create a generic array in Java?
(32 answers)
Array of Generic List
(5 answers)
Closed 9 years ago.
For various reasons I want to convert a list to an array, however the Collection contains objects that are themselves generics.
I have tried the following four options to get it to compile without needing a #supressWarnings('unchecked') annotation, but none of them work. Is there a solution to make this work correctly, or am I forced to use the annotation?
Iterator<T>[] iterators;
final Collection<Iterator<T>> initIterators = new ArrayList<Iterator<T>>();
// Type safety: Unchecked cast from Iterator[] to Iterator<T>[]
iterators = initIterators.<Iterator<T>>toArray(
(Iterator<T>[])new Iterator[initIterators.size()]);
// Type safety: Unchecked invocation toArray(Iterator[]) of the generic
// method toArray(T[]) of type Collection<Iterator<T>>
// Type safety: The expression of type Iterator[] needs unchecked conversion
// to conform to Iterator<T>[]
iterators = initIterators.<Iterator<T>>toArray(
new Iterator[initIterators.size()]);
// Type safety: The expression of type Iterator[] needs unchecked conversion
// to conform to Iterator<T>[]
iterators = initIterators.toArray(new Iterator[initIterators.size()]);
// Doesn't compile
iterators = initIterators.toArray(new Iterator<T>[initIterators.size()]);
There is no type-safe way to create an array of a parameterized type such as Iterator<T>[].
Alternatively, you can create a raw array: Iterator<?>[]. Or, if you can avoid the use of arrays entirely, use a collection type like List<Iterator<T>>.
The reason it is not possible is that Java arrays are covariant and the parameterized bounds of Generic types are invariant. That is to say:
Integer[] integers = new Integer[1];
Number[] numbers = integers; // OK because Integer extends Number
numbers[0] = new Double(3.14); // runtime exception
The compiler allows the assignment because Double extends Number and the declared type of numbers is Number[]. But at runtime the actual array object instance is the original Integer[1] and arrays know the type of the objects they contain.
With generics, parameterized types are different. For one, due to compile-time type erasure they do not intrinsically know their runtime types.
List<Integer> integerList = new ArrayList<Integer>();
List<Number> numberList = integerList; // compiler error, prevents:
numberList.add(new Double(3.14)); // would insert a Double into integerList
Collection<Integer> integerCollection = integerList; // allowed
// OK because List extends Collection and the <type parameter> did not change
Collection<Number> numberCollection = integerList; // compiler error
// an "Integer" is a "Number"
// but "a collection of Integers" is more specific than "a collection of Numbers"
// and cannot be generally treated the same way and guarantee correct behavior
List<?> rawList = integerList; // allowed, but...
rawList.add(new Integer(42)); // compiler error, Integer is not a ... a what?
With generics, in Java, you are relying on the compiler (not the runtime) to validate that the generic types are correct and safe.
So while an Iterator<?>[] knows at runtime that it is an array that contains Iterator elements, the <T> in Iterator<T>[] is erased at compile time and the runtime has no way to know what it was supposed to be. So you get an unchecked warning.

Creating an array to store generic types in Java [duplicate]

This question already has answers here:
Generic arrays in Java
(5 answers)
Closed 9 years ago.
Suppose I have to create an array which stores ArrayList's of Integers and the array size is 10.
The below code will do it:
ArrayList<Integer>[] pl2 = new ArrayList[10];
Question 1:
In my opinion the more appropriate code would be
ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];
Why does this not work?
Question 2:
Both of the below compile
ArrayList<Integer>[] pl2 = new ArrayList[10];
ArrayList[] pl3 = new ArrayList[10];
What is the difference as far as the reference declaration of pl2 and pl3 is concerned?
The generic info only matters in compile time, it tells the compiler which type could be put into an array, in runtime, all the generic info will be erased, so what matters is how you declare the generic type.
Quoted from Think in Java:
it’s not precisely correct to say that you cannot create arrays of
generic types. True, the compiler won’t let you instantiate an array
of a generic type. However, it will let you create a reference to
such an array. For example:
List<String>[] ls;
This passes through the compiler without complaint. And although you
cannot create an actual array object that holds generics, you can
create an array of the non-generified type and cast it:
//: arrays/ArrayOfGenerics.java
// It is possible to create arrays of generics.
import java.util.*;
public class ArrayOfGenerics {
#SuppressWarnings("unchecked")
public static void main(String[] args) {
List<String>[] ls;
List[] la = new List[10];
ls = (List<String>[])la; // "Unchecked" warning
ls[0] = new ArrayList<String>();
// Compile-time checking produces an error:
//! ls[1] = new ArrayList<Integer>();
// The problem: List<String> is a subtype of Object
Object[] objects = ls; // So assignment is OK
// Compiles and runs without complaint:
objects[1] = new ArrayList<Integer>();
// However, if your needs are straightforward it is
// possible to create an array of generics, albeit
// with an "unchecked" warning:
List<BerylliumSphere>[] spheres =
(List<BerylliumSphere>[])new List[10];
for(int i = 0; i < spheres.length; i++)
spheres[i] = new ArrayList<BerylliumSphere>();
}
}
Once you have a reference to a List[], you can see that you
get some compile-time checking. The problem is that arrays are
covariant, so a List[] is also an Object[], and you can use
this to assign an ArrayList into your array, with no error at
either compile time or run time.
If you know you’re not going to
upcast and your needs are relatively simple, however, it is possible
to create an array of generics, which will provide basic compile-time
type checking. However, a generic container will virtually always be a
better choice than an array of generics.
Question 1:
Basically, this is forbidden by Java language. This is covered in Java Language Specification for generics.
When you use
ArrayList<Integer>[] pl2 = new ArrayList[10]; // warning
you get the compiler warning, because the following example will compile (generating warning for every line of code):
ArrayList wrongRawArrayList = new ArrayList(); // warning
wrongRawArrayList.add("string1"); // warning
wrongRawArrayList.add("string2"); // warning
pl2[0] = wrongRawArrayList; // warning
but now you array, that supposed to contain ArrayList of Integer, contains totally wrong ArrayList of String objects.
Question 2:
As it was already answered, declaration of p12 provides you with compile time checking and frees you from using casting when getting items from your ArrayList.
Slightly modified previous example:
ArrayList<Integer>[] pl2 = new ArrayList[10]; // warning
ArrayList<String> wrongArrayList = new ArrayList<String>(); // OK!
wrongArrayList.add("string1"); // OK!
wrongArrayList.add("string2"); // OK!
pl2[0] = wrongArrayList; // ERROR
Now, since you are using generics, this won't compile.
But if you use
ArrayList[] pl2 = new ArrayList[10];
you will get the same result as in the first example.
Arrays are covariant. That means they retain the type of their elements at runtime. Java's generics are not. They use type erasure to basically mask the implicit casting that is going on. It's important to understand that.
You need to use Array.newInstance()
In addition, arrays carry runtime type information about their
component type, that is, about the type of the elements contained.
The runtime type information regarding the component type is used when
elements are stored in an array in order to ensure that no "alien"
elements can be inserted.
For more details look here
This does not work because generic classes does not belong to Reifiable Types.
The JLS about Array creation expression states :
It is a compile-time error if the [class type] does not denote a reifiable type (§4.7). Otherwise, the [class type] may name any named reference type, even an abstract class type (§8.1.1.1) or an interface type (§9).
The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard.
The definition of Reifiable Types is :
Because some type information is erased during compilation, not all types are available at run time. Types that are completely available at run time are known as reifiable types.
A type is reifiable if and only if one of the following holds:
It refers to a non-generic class or interface type declaration.
It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
It is a raw type (§4.8).
It is a primitive type (§4.2).
It is an array type (§10.1) whose element type is reifiable.
It is a nested type where, for each type T separated by a ".", T itself is reifiable.
For example, if a generic class X<T> has a generic member class Y<U>, then the type X<?>.Y<?> is reifiable because X<?> is reifiable and Y<?> is reifiable. The type X<?>.Y<Object> is not reifiable because Y<Object> is not reifiable.
Let's start with question 2 first and then get back to question 1:
Question 2:
>
ArrayList[] pl2 = new ArrayList[10];
ArrayList[] pl3 = new ArrayList[10];
What is the difference as far as the reference declaration of p12 and
p13 is concerned?
In pl2 ensures better type safety than p13.
If I write for pl2:
pl2[0]=new ArrayList<String>();
it will give me a compiler error stating "cannot convert from ArrayList<String> to ArrayList<Integer>"
Thus it ensures compile time safety.
However if I write for p13
pl3[0]=new ArrayList<String>();
pl3[1]=new ArrayList<Integer>();
it will not throw any error and the onus will be on the developer to code and check properly while extracting data from p13, to avoid any unsafe type conversion during runtime.
Question 1:
That's just probably the way generics work. During the main array initialization, ArrayList<Integer>[] pl2 = new ArrayList[10], the left hand side, ArrayList<Integer>[] pl2, will ensure type safety only when you initialize the ArrayList object in the index position:
pl2[0]=new ArrayList<Integer>();
The right hand side main array declaration = new ArrayList[10] just ensures that the index position will hold ArrayList type items. Also have a look at type erasure concepts in Type Erasure for more information.
Question 1.
Well, it's not the correct syntax. Hence that does not work.
Question 2.
ArrayList<Integer>[] pl2 = new ArrayList[10];
ArrayList[] pl3 = new ArrayList[10];
Since pl2 is defined with generic type <Integer> at compile time, the compiler will be know that pl2 is only allowed to have Integers and if you try to assign somthing other than Integers you will be alerted and compilation will fail.
In pl3 since there is no generic type you can assign any type of object to the list.
ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];
Means you don't need to do casting when you retrive data from the ArrayList
example
in normal case
ArrayList[] pl2 = new ArrayList[10];
pl2.put(new Integer(10));
Integer i = p12.get(0); // this is wrong
Integer i = (Integer)p12.get(0); // this is true with casting
but
ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];
pl2.put(new Integer(10));
Integer i = p12.get(0); // this is true no need for casting
Problems with generics are by default issued as a warning by the compiler.
After compilation, because of type erasure, they all become ArrayList[] pl2 = new ArrayList[10], but the compiler warns you that this is not good.
Generics have been added to Java, and to be backwards compatible you can use generic with non-generic interchangeably.
Question1
You cannot create arrays of parameterized types
Question 2
ArrayList<Integer>[] pl2 = new ArrayList[10];
It means you are telling to compiler that you are going to create array which will store arraylist of integers. Your arraylist will only contain Integer objects. That's where generics comes in. Generics make your code more safer and reliable. If you are sure your list should only contain integer objects, you should always go ahead with this.
But when you say
ArrayList[] pl3 = new ArrayList[10];
it means arraylist can store any object type like string, integer, custom objects, etc.
It seems like you cannot create an array of arraylists with a generic type, according to an answer to Stack Overflow question Create an array of ArrayList elements.
As far as I know, in Java there are no such things as generics. In terms of types, ArrayList<Integer> and ArrayList are the same things.
Java uses type erasure for generics. It means that all type information about the generic is erased at compile time. So ArrayList<Integer> become ArrayList.
So it's just a compile-time trick. I am guessing, to avoid any confusions or mistakes that the programmer might do, they allowed ArrayList<Integer>[] to be instantiated like this: new ArrayList[10].
So an ArrayList<Integer>[] and a ArrayList[] are the same thing because the information in brackets is erased at compile time.

how to instanciate a generic collection array?

Why this code comes out an error?
List<String>[] l = new ArrayList<String>[10];
Eclipse said I cannot create a generic array, but in fact I am trying to create a collection array.
It has naught to do with collections. The (raw) array constructor cannot take a parameterised type.
Try
List[] l = new ArrayList[10];
(Thanks #newacct)
Please try like this
List<String>[] al = new ArrayList[10];
al[0] = new ArrayList<String>();
al[1] = new ArrayList<Date>(); // Give u error because List accepts only String
Per the language spec, section 15.10:
"An array creation expression creates an object that is a new array whose elements are of the type specified by the PrimitiveType or ClassOrInterfaceType. It is a compile-time error if the ClassOrInterfaceType does not denote a reifiable type."
And per the language spec, section 4.7:
Because some type information is erased during compilation, not all types are available at run time. Types that are completely available at run-time are known as reifiable types.
A type is reifiable if and only if one of the following holds:
It refers to a non-generic class or interface type declaration.
...
So put simply, "because the language says you can't". I understand the ultimate cause dealt with maintaining backward compatibility, but I'm not familiar with the details.
Try it like this : List[] l = new List[10];
Then add ArrayList(s) to your array of List...
The simple array cannot hold generics information. It was included in the language long before generics...

Array of Generic List

I am playing with Generic and arrays, it seems the following code compiles fine,
ArrayList<Key> a = new ArrayList<Key>();
But the compiler complains about this one,
ArrayList<Key>[] a = new ArrayList<Key>[10];
By reading post in stackoverflow, I sort of understand that this is due to Type Erasure and I can fix it by using,
ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10];
or list of list
ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>();
But I can't figure out the reason behind the scene. Especially, why the second one is illegal given the first one is perfectly OK. And why the compiler does not complain about the list of list.
You can't have an array, because an array requires a raw type. You typecast it in the second instance, which makes it fit the defined type, and is therefore legal (however, this is impossible for it to infer). The list of list is legal as ArrayList isn't an array.
Read chapter 7.3 (page 15) in the official tutorial for more details on this.
The component type of an array object may not be a type variable or a
parameterized type, unless it is an (unbounded) wildcard type.You can
declare array types whose element type is a type variable or a
parameterized type, but not array objects.
This is annoying, to be sure. This restriction is necessary to avoid situations like:
List<String>[] lsa = new List<String>[10]; // not really allowed
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // unsound, but passes run time store check
String s = lsa[1].get(0); // run-time error - ClassCastException
If arrays of parameterized type were allowed, the example above would
compile without any unchecked warnings, and yet fail at run-time.
The tutorial then goes on to say the following:
Since type variables don’t exist at run time, there is no way to determine what the
actual array type would be.
The way to work around these kinds of limitations is to use class literals as run time
type tokens
Array was poor man's generics; with real generics, one should avoid arrays, though not always possible.
Arrays are covariant, generics are invariant; combined with erasure, things just don't fit very well, as illustrated by the example in Chris's answer.
However I think it is possible to relax the spec to allow generic array creation - there's really no problem there. The danger comes when up casting the array; a compiler warning at that point is enough.
Actually Java does create generic arrays for vararg methods, so it's a little hypocritical.
Here are utility methods taking advantage of that fact
#SafeVarargs
static <E> E[] arrayLiteral(E... array)
{
return array;
}
#SafeVarargs
static <E> E[] newArray(int length, E... array)
{
return Arrays.copyOf(array, length);
}
// usage
List<String>[] array1 = arrayLiteral(list, list);
List<String>[] array2 = newArray(10);
I had a similar question myself - FWIW, I didn't find the answers persuasive. The pertinent section from the most detailed answer (referring to the pdf reference) is this:
The component type of an array object may not be a type variable or a
parameterized type, unless it is an (unbounded) wildcard type.You can
declare array types whose element type is a type variable or a
parameterized type, but not array objects. This is annoying, to be
sure. This restriction is necessary to avoid situations like
List<String>[] lsa = new List<String>[10]; // not really allowed
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // unsound, but passes run time store check
String s = lsa[1].get(0); // run-time error - ClassCastException
So because I can cat the List[] to Object[], then shove something incorrect into the Object[], then refer to incorrectly from the List reference, through the casted ref, this is bad/disallowed? But only with new?
It's still more than a bit obscure to me how declaring this with new is any more or less of a problem than the usage, still crossing my eyes staring at it in the hope that it will start to make sense, or at least resolve into a nice 3d image.
Creating generic arrays isn't type-safe (see "Item 25: Prefer lists to arrays" of "Effective Java - second edition" by Joshua Bloch).
Use:
List<List<Key>> b = new ArrayList<List<Key>>(10);
Or with Java SE 7:
List<List<Key>> b = new ArrayList<>(10);
The arrays allow to escape type checks (as illustrated in the Chris's answer). So, you could have a code which passes all compiler checks (no "unchecked" warnings from compiler), but fail at run time with ClassCastException.
Forbidding this construction raises the problem for a developer, so warnings do appear.

Categories

Resources