Array of generic holders - java

If I have a map of Entry .. objects , and I have an array in a class
private Entry<K,V> array;
Can I say
array = new Entry[someInt];
which I've done, or do I need a typecast like my instructor says is necessary such as
array = (Entry<K,V> E[]) new Entry[someInt];
Note that the first one did work when I ran my JUnits.

Have you thought about doing a List ?
Entry<K,V> array = new ArrayList<Entry<K,V>>();
array.add(new HashMap<K,V>());

You can do either one. Implicit conversions to and from raw types are allowed without a cast.
array = new Entry[someInt]; will produce an unchecked conversion warning.
array = (Entry<K,V>[])new Entry[someInt]; will produce an unchecked cast warning.
So neither one is really better than the other.
Note that if you create the array with the wildcard type, you will need to have a cast:
array = (Entry<K,V>[])new Entry<?,?>[someInt]; // compiles
array = new Entry<?,?>[someInt]; // doesn't compile

Related

Cannot add an object to List<?> instantiated as ArrayList<Object>

I ask this question because of a discussion about one answer here on Stack. The statement is the following:
Given the following code:
List<?> list =new ArrayList<Integer>();
Why can't we do:
Integer e = 2;
list.add(e);
This throws a compiler error, despite the fact that we instantiated the list as an ArrayList<Integer>.
Why is that ?
Because a List<?> could be any sort of List (List<String> for example). And the compiler should not permit adding the wrong type to a list.
However, if you know the actual class then you can do a class cast at runtime:
((List<Integer>)list).add(e);
Code like this should be avoided since it can generate a ClassCastException if an unexpected type is encountered at runtime. To make matters worse (as noted by luk2302), our ClassCastException might only occur in an entirely different area of the code-- namely, when we are retrieving something from the list.
A better approach
If you know that the list will be of a specific type or a superclass of that type, then you could define the variable using a bounded wildcard:
List<? super Integer> list;
Integer e = 2;
list = new ArrayList<Integer>();
list.add(e);
list = new ArrayList<Number>();
list.add(e);
list = new ArrayList<Object>();
list.add(e);
This approach, as noted by M. Prokhorov, allows us to avoid the need for an inadvisable cast.
Just create an Arraylist of and they will let you add all, because Integer, String and Boolean are child or in other words Object class is their parent.

Using an Array as table in custom generic Hashtable to store entries

I am experimenting with making my own custom Hashtable as a way of understanding the data structure, and have run into what many other people seem to have run into; that you cannot create a generic array the same way you would make another array. I understand the reasons for this, but also know that Java's HashMap itself uses an array to store Entry items. If my understanding is correct, Java's HashMap creates an object[] and then casts each element of the object to the Entry class each time the put or get method is called. Is this correct?
I have read answers about generic arrays saying it is possible to instead do something like having Entry[] table as a class variable and then using table = (Entry[]) new Object[size]; in the constructor as a way of avoiding having to do the casting in both the put and get methods, but this does leads to a ClassCastException, which is understandable since it would have to check each element of the Object array anyway in order to make sure they are the Entry class. Does this mean I cannot use this method in my custom Hashtable?
Finally, another way of creating the Entry array seems to be checking the class type in the constructor and using Entry[] table = (Entry[]) Array.newInstance(c, s); Is this method perhaps more suitable?
Below is a snippet of my own code relevant to this question. I am wondering if my interpretation of everything above is correct, and if this is an acceptable way of going about my own Hashtable. I also understand my method of using determining the index for the given hashCode may be incorrect, but that is outside the scope of my question :), and my put and get methods are definitely incomplete!
public class HashTable<K, V> {
Object[] buckets;
HashTable(int size) {
buckets = new Object[size];
this.size = size;
}
void put(K key, V value) {
int i = key.hashCode()%size;
buckets[i] = (Entry) new Entry(key, value, (Entry) buckets[i]);
}
K get(K key) {
int i = key.hashCode()%size;
Entry entry = (Entry) buckets[i];
return entry.key;
}
}
If my understanding is correct, Java's HashMap
creates an object[] and then casts each element of the object to the
Entry class each time the put or get method is called. Is this
correct?
The standard library's source is available. You could check it for yourself. If you did, you would find that no, that's not quite what java.util.HashMap does.
I have read answers about generic arrays saying it is possible to
instead do something like having Entry[] table as a class variable and
then using table = (Entry[]) new Object[size];
To the extent that such answers recommended exactly what you describe, they are wrong. I suspect, however, that your "something like" does not capture the key elements of the answers you saw.
There are two potential issues
Creating an array whose element type is drawn from a type parameter:
class MyClass<T> {
// CAN'T DO THIS:
T[] array = new T[2];
// can do this:
T[] array = (T[]) new Object[2];
// or this:
Object[] array = new Object[2]; // (and cast later)
}
Creating an array whose element type is parameterized
class MyOtherClass<T> {
// CAN'T DO THIS, EITHER:
SomeType<T>[] array = new SomeType<T>[2];
// can do this:
SomeType<T>[] array = (SomeType<T>) new SomeType[2];
// or this:
SomeType[] array = new SomeType[2]; // (and cast later)
}
As you will have seen in the JDK source (you did follow the above link, right?), HashMap's issue is of the second type, and what it does is create an array of the appropriate raw type, and then cast that to the desired parameterized type -- which will trip the compiler's type safety warnings, but is in fact perfectly type safe as long as no other, raw or differently parameterized, reference escapes.
in the constructor as a
way of avoiding having to do the casting in both the put and get
methods, but this does leads to a ClassCastException [...]. Does this
mean I cannot use this method in my custom Hashtable?
Yes, of course it does. The method you describe and demonstrate is invalid, as the exception tells you. An Object[] is not an Entry[]. But that's not what the answers you reviewed were suggesting you do.
Finally, another way of creating the Entry array seems to be checking
the class type in the constructor and using Entry[] table = (Entry[])
Array.newInstance(c, s); Is this method perhaps more suitable?
Rarely is reflection a better answer for anything. It only makes sense when you don't have all the type information you need at compile time, and that is not your case.
It looks like your Entry class is an inner class, which I'd recommend against because it makes things more complicated. First, let's just assume that we don't have an inner class.
For the illustration, we have a simple generic class:
class Foo<T> {}
There's a difference between these two generic array types:
class Container<T> {
// creating an array with erasure of T[]
// vvvvvvvvvvvvv
T[] arrA = (T[]) new Object[N];
// creating an array with erasure of Foo<T>[]
// vvvvvvvvvv
Foo<T>[] arrB = (Foo<T>[]) new Foo[N];
// Note that the following would be slightly
// better because it doesn't use a raw type,
// but it doesn't work for this illustration
// because it's not the erasure of Foo[]:
// (Foo<T>[]) new Foo<?>[N];
}
Casting checks the erasure of type, so suppose we create a new container and assign those arrays to something in the outside world:
Container<String> c = new Container<String>();
String[] arrA1 = c.arrA;
Foo<String>[] arrB1 = c.arrB;
// After erasure these assignments become:
String[] arrA1 = (String[]) arrA;
Foo[] arrB1 = arrB;
The first assignment, arrA1 = c.arrA throws a ClassCastException, but the second assignment, arrB1 = c.arrB does not. This is because in the first case the conversion is from Object[] to String[] whereas in the second case there is no checked cast because all parameterizations of Foo<T> just become Foo after erasure.
This is all to explain my next point which is that creating an array of a parameterized type is more acceptable than creating an array of a type variable. In the case of the type variable array we have an Object[] masquerading as a T[] but in the case of the parameterized type we actually do have an array of Foo[], it's just that there is no checking for the type arguments to Foo. In other words:
Container<String> c = new Container<String>();
// Recall that this assignment doesn't throw a ClassCastException
Foo<String> arrB = c.arrB;
Object[] arrBAsOBj = arrB;
// This assignment throws an ArrayStoreException
arrBAsObj[0] = new StringBuilder();
// This assignment does not throw an ArrayStoreException
arrBAsObj[0] = new Foo<Integer>();
Although, I'd like to note that you should never expose a generic array to the outside world. I'm just doing that to illustrate the explanation.
Anyway, if you're writing something like a hash table, it's acceptable to create an unchecked array of a parameterized type. I usually write a helper method like this:
private static <K, V> Map.Entry<K, V>[] createUncheckedArray(int length) {
#SuppressWarnings("unchecked")
final Map.Entry<K, V>[] unchecked =
(Map.Entry<K, V>[]) new Map.Entry<?, ?>[length];
return unchecked;
}
Just don't return it to the outside world, because we still don't actually have a generic array, just an array of Map.Entry with unchecked type arguments.
Really Java should just have a simple class like Array<T> for this sort of case when we actually need a fixed-length container.
For an inner class you have to use a parameterized type as a qualifier, something like this:
private Entry[] createUncheckedArray(int length) {
#SuppressWarnings("unchecked")
final Entry[] unchecked =
(Entry[]) new HashTable<?, ?>.Entry[length];
return unchecked;
}

Getting list values using Iterator Java

I'm trying to use a list iterator to walk a linked list and do some operations / checks on the next node depending on the integer value stored there, but I'm getting some errors in my code. I think I'm not understanding what iterator.next() is returning (some E object, but I don't know how to access the value I want from it) The editor wants me to do some casting as explained below. It gets rid of errors, but I don't know if this is a safe way to handle the problem or if it has the behavior I'm looking for. Please explain why I am getting errors and if there is a good way to handle this.
LinkedList<Integer>[] hash = new LinkedList[list.size()];
hash = remo.createHash(hash, list.size());
ListIterator iterator = list.listIterator(0);
// use the value of the integer stored at the next Node as its hash
// and add the same value to the linked list at that bucket
int i = 0;
while(iterator.hasNext()){
hash[iterator.next()].add(iterator.next());
i++;
}
// reset iterator to beginning of list
iterator = list.listIterator(0);
// if the hash bucket corresponding to the value at that node has more than
// one item in its list, remove that node from the list.
while(iterator.hasNext()){
if(hash[iterator.next()].size()>1){
iterator.remove(iterator.next());
}
}
createHash initializes each linked list in the array and remo is an instance of my class.
the editor wants me to cast iterator.next() to an int hash[iterator.next()] and it wants me to cast it to an in .add(iterator.next()).
Example:
hash[(int)iterator.next()]
hash[(int)iterator.next()].add((Integer)iterator.next());
LinkedList<Integer>[] hash = new LinkedList[list.size()];
This line is problematic due to http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#createArrays
You cannot create arrays of parameterized types. For example, the following code does not compile:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
Because:
Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>(); // OK
stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown,
// but the runtime can't detect it.
If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.
As such, you are creating an array of lists that aren't using generics (as you cannot create arrays of parameterized types), and as such, it stores Objects (it doesn't know what type you're actually planning to store). You should probably use an ArrayList instead of an Array to fix this problem, like so:
List<List<Integer>> listOfLists = new ArrayList<List<Integer>>(list.size());
//example usage
listOfLists.add(new LinkedList<Integer>());
for(List<Integer> currList : listOfLists)
{
...
}
Arrays and generics don't mix. Just use a List<List<Integer>>:
List<List<Integer>> hash = new LinkedList<List<Integer>>(list.size());

How can an Integer be added to a String ArrayList?

List list = new ArrayList<String>() ;
list.add(1) ;
Integer hello = (Integer) list.get(0) ;
System.out.println(hello);
The above code has a reference of type List referring to an instance of ArrayList of type String. When the line list.add(1) is executed, isn't the 1 added to the ArrayList (of type String) ? If yes, then why is this allowed?
You have used type erasure, which means you have ignored previously set generic checks. You can get away with this as this as generics are a compile time feature which isn't checked at runtime.
What you have the same as
List list = new ArrayList() ;
list.add(1) ;
Integer hello = (Integer) list.get(0) ;
System.out.println(hello);
or
List<Integer> list = new ArrayList<Integer>() ;
list.add(1) ;
Integer hello = list.get(0); // generics add an implicit cast here
System.out.println(hello);
If you look at the byte code generated by the compiler, there is no way to tell the difference.
Interestingly, you can do this
List<String> strings = new ArrayList<String>();
#SuppressWarnings("unchecked");
List<Integer> ints = (List) strings;
ints.add(1);
System.out.println(strings); // ok
String s= strings.get(0); // throws a ClassCastException
The problem is that your list variable has a raw type and you can add objects of any type to this list. To solve the problem just declare it as a List of String's:
List<String> list = new ArrayList<String>() ;
It compiles because the declaration List list uses the raw type List, not the bound type List<String>. The second line compiles because your list variable can accept anything. The third line compiles because list.get(0) returns Object. The fourth line has no reason to fail.
By accident, nothing in the compiled code caused the Integer 1 to be cast to a String. But you must not depend on that. Had the first line been the proper
List<String> list = new ArrayList<>();
your mistake would have been caught at compile time.
When you declare a list like this:
List list = new ArrayList<String>() ;
You are using what's called a Raw Type. It's a type that has a generic type parameter, like List, but you failed to supply one. If you check the compiler warnings you will see it is telling you about this. Mixing parameterized types with raw types is generally considered a programming error. This is ok:
List<String> list = new ArrayList<String>() ;
and this is ok (but obsolete):
List list = new ArrayList();
But the way you wrote it should be avoided.
The problem is generics are checked only at compiler time, and you told the compiler List, not List<String>, so it will let you put anything you want in there! The fact that the right side has the parameter doesn't really mean anything, it's the type of the actual reference that matters to the compiler.
List list = new ArrayList<String>() ; // WARNING!!!
An unsafe List refers to a safe ArrayList
list.add(1) ; // WARNING!!!
An Integer (int 1 converted to Integer is Autoboxing) not a String added to the List
Integer hello = (Integer) list.get(0) ;
Casting is required because unsafe List can contain anything.
System.out.println(hello);
Integer.toString() called
When the line list.add(1) is executed, isn't the 1 added to the ArrayList (of type String) ?
Yes. Integer was added to an ArrayList that is supposed to contain String.
If yes, then why is this allowed?
Backward Compatibility. Java has to support pre-generic codes.
List list; means it is unsafe so you can do anything with it and the referred ArrayList() has to tolerate that.
Moral: Don't ignore the compiler warnings otherwise Integer might enter into an ArrayList that is supposed to contain Strings.
Yes... the 1 is addeded to Array, because Java, like other languages, implements implicit (automatic) conversion (casting) of primitives data types.
More information here Conversions and Promotions
Try this
List.add(Integer.toString(1));
You also can try this
List list = new ArrayList<Object>() ;
If there is no need that the arraylist must be a Arraylist of String
No, because Java sees the 1 as an Integer. You will need to convert your number as a String first with Integer.toString(n)

Java generic object with varying types.

Imagine having 2 Generic ArrayLists each storing different types. My program will be using only one of these ArrayLists at a time. Is it possible to create a general ArrayList (currentArrayList) which can store both ArrayLists, and use the two ArrayLists without casting.
ArrayList<Integer> arrInt = new ArrayList<>();
arrInt.add(10);
ArrayList<String> arrString = new ArrayList<>();
arrString.add("ten");
ArrayList<XXX> currentArrayList = arrInt;
Integer i = currentArrayList.get(0);
currentArrayList = arrString;
String str = currentArrayList.get(0);
Thanks.
The problem is the part where you want to:
use the two ArrayLists without casting.
Definitely not going to happen. The whole point of a static type system is to stop you from being able to treat a general type as a specific type without explicitly asking to do so.
So for example, you could say:
ArrayList<?> currentArrayList = arrInt;
Object i = currentArrayList.get(0);
But notice that i is just an Object, so the compiler is happy - we don't know anything about the things in the array except that they must be Object-based because this is a Java program. But you want to say:
ArrayList<?> currentArrayList = arrInt;
Integer i = currentArrayList.get(0);
It's that second line that isn't ever going to happen without a cast.
UPDATE Question from comments:
Can't the compiler easily infer the type of the ArrayList by
looking at the generic type of arrInt ?
Suppose it did that for us. What should it then do with the type of currentArrayList when it sees the line:
currentArrayList = arrString;
Your code as it stands assumes that the compiler is not going to do that kind of inference. Suppose you comment-out that second assignment. Now the compiler could make the inference you suggest, and allow your code to compile. But then in the future if you put back the second assignment, the rest of your code would stop compiling! This would be needlessly confusing.
You have to determine the type of the object anyway eventually.
i guess you want to avoid "supresswarning("casting")" kind of warning?
if ture, here it goes:
ArrayList<Object> list = new ArrayList<Object>();
list.add(1);
list.add("abc");
list.add(new Double(3.1415926));
list.add(true);
for (Object o : list) {
// or more accurate
String type;
if (o instanceof Integer) {
type = "int";
// logic here...
int i = (Integer) o; // no casting warning
} else if (o instanceof String) {
type = "string";
// logic here...
}
// other possibilities if you want
else { // unmatched
type = o.getClass().getSimpleName();
}
System.out.println(type + ": " + o);
}
Misunderstood the question - why on earth would you want to do that?
You are not creating a new ArrayList you are just using a Pointer without the type - its still the same ArrayList!
ArrayList<ArrayList<Object>> array = new ArrayList<ArrayList<Object>>();
array.add(new ArrayList<Integer>());
array.add(new ArrayList<String>());
This should do the trick, since all other possible Objects will inherit from Object in hence be accepted.

Categories

Resources