Collection with generics - java

I have a question about using generics with collections.
ArrayList<Integer> al=new ArrayList<Integer>();
We know that the above line means that ArrayList al is restricted to hold only integers. So the following line gives a compilation error:
al.add("wwww");
But I don't understand what the below line means,
ArrayList al=new ArrayList<Integer>();
Where we don't give ArrayList<Integer> at the left side while declaring. Now the following line doesn't give a compilation error:
al.add("wwww");
So if I declare like
ArrayList al=new ArrayList<Integer>();
that means a1 can accept any types?
What's the difference between those two declarations?

The latter declaration (without generic type) is obsolete and deprecated. You shouldn't use it, it compiles only for backward compatibility. Modern IDEs will generate warning here.
Also note that Java only enforces generic types at compile time, so technically you can add incorrect type to a collection with some extra casts. That's why it is better to stay with generics all the time and do not bypass them.

With this code:
ArrayList al = new ArrayList<Integer>();
a1.add("www");
the compiler will generate a warning (which you should heed!) but the code will run without error. The problem comes on extracting data from a1. If you "know" that it contains Integer values, you can just do this:
Integer val = (Integer) a1.get(index);
But when you hit the "www" element you're going to get a ClassCastException. Generics are meant to move such errors to compile time instead of forcing the poor developer to track down how a String value ended up in that Integer array list.

The latter declaration is basicly the same as:
ArrayList<Object> al=new ArrayList<Object>();
thus accepting any Object. It should not be used, and it is possible to use so that Java is backward compatible (pre Java1.5).

you should write like this ArrayList<Integer> al=new ArrayList<Integer>(); to avoid adding to your list objects with another types because you'll see the error compile time. Of course you can use ArrayList al=new ArrayList<Integer>(); but then you must be careful. In another words, always use the first one :D

Generics are implemented by type erasure: generic type information is present only at compile time, after which it is erased by the compiler. So, ArrayList al declaration let compiler accept any type. Here is java doc on this topic.

This kind of declaration is allowed for backward compability. So you can for example pass you generic list to the method from old-style library wich accepts only raw list:
List<Integer> list = new ArrayList<Integer>();
OldStyledClass.method(myArray);
where method is declared like:
public static void method(List list)...
In this case you have some special utility methods in java.util.Collections. So you can pass to this method protected list:
OldStyledClass.method(Collections.checkedList(myArray, Integer.class));
If method will try to put into your list object of some other type you will get ClassCastException immediatley.

Related

How were types handled without generics in ArrayList before java version 1.5 [duplicate]

This question already has answers here:
ArrayList Generic without Type
(5 answers)
Closed 5 years ago.
I have a question about the history of Java.
Java has had ArrayList since 1.2.
Java has generics since version 1.5.
How was the implementation of ArrayList without generics to define the type?
It was all done with Object (and there was a lot of casting involved), e.g.:
ArrayList list = new ArrayList();
list.add(new Thingy());
// ...
Thingy t = (Thingy)list.get(0);
// Note ---^^^^^^^^
The list only knew what it stored was Object, it was up to the code using the list to cast back to a useful type.
As you can imagine, this lead to all sorts of unpleasantness — you could put the wrong kind of object in the list, and then get ClassCastExceptions later when you tried to cast it to the type you were expecting; if you changed what was in the list, you had to change your casts everywhere and inevitably forget one, etc., etc. Generics helped remove those pain points.
Before generics, ArrayList have the Object as a type so that you can insert and get back the type Object which mean that you need to cast them while retrieving back to respected type. After generics these kind of redundant code got removed.
If you have close look at generic docs, your specific question been answered
Elimination of casts.
The following code snippet without generics requires casting:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
When re-written to use generics, the code does not require casting:
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast
Actually you should not ask these type of questions in stackoverflow.What everyone expects is somecode from you to debug..But anyway you seems to be like new to stackoverflow...
You can use arraylist without generics too.
For example if you use generics to hold items of particular type it looks like this..
ArrayList<String> list=new ArrayList<>();
If you don't want to use generics you can simply use this
ArrayList list =new ArrayList();
Let me tell you the very big disadvantage of using without generics.If you don't use generics then it assumes every thing as objects.So you need to type cast these to your particular datatype each time you retreive elements.
For example
for (Object o:list)
{
String s=(String)o;
System.out.println(s);
}
is what you have to do if you dont use generics..
If you use generics
then
for(String s:list)
{
System.out.println(s)
}
Hope this helps....:)

what is the difference between arraylist with and without <> operator

I giving a mock test on SCJP. I encounter two different question having the statements as
ArrayList<Integer> arr = new ArrayList<Integer>();
and
ArrayList arr = new ArrayList();
1) What is the differnce between these two?
My Analysis=> first can store Integerand its subclasses. and the second can store Object and its subclasses.
2) Can we make object without <> of any generic class?
1) What is the differnce between these two?
You already find the answer yourself. I suggest you to go deeper in the Java documentation to look for details...
2) Can we make object without <> of any generic class?
IMO, you should always use the first approach:
ArrayList<Integer> arr = new ArrayList<Integer>();
or better
List<Integer> arr = new ArrayList<Integer>();
because you make clear to the reader what you intent to put in the ArrayList.
1) What is the differnce between these two?
ArrayList<Integer> arr = new ArrayList<Integer>(); This list store Integer class objects.
ArrayList arr = new ArrayList(); - This list is generic list it will store all type of objects.
2) Can we make object without <> of any generic class?
<> is a generics which introduced by Java 5
Generics add stability to your code by making more of your bugs
detectable at compile time.
when you are adding object in collection its become error prone when you don't know which type of objects your collection takes. and which type of object you have to retrieve from that collection.
So, It's better to use generics, Read more about Generics
Short answer: ArrayList without <> is basically equivalent to ArrayList<?> which means that the type of the List's content in not known at compile time.
Due to type erasure at runtime there's no difference between the generic and non-generic version: they are just list containing objects.
What you gain using the generic (with <>) version is that the compiler can do some static type checking assuring that your code is sound at least at the type level.
As per your second question, yes. You can create non-generic version of any generic class, although this is generally a bad practice both for the clarity of your code as well as its safeness.

Array of Lists, and workarounds

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.

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

What could happen when one of the type parameters was not specified during instantiation of a collection?

The instantiation of a collection in Java is normally as below:
ArrayList<Integer> ali = new ArrayList<Integer>();
It is said that with this convention, certain errors such as
String s = (String)ali(0)
Can lead to compile error instead of run time exceptions.
However, I observed that although
ArrayList ali = new ArrayList<Integer>();
Will cause the situation above to cause run time exceptions,
ArrayList<Integer> ali = new ArrayList();
Will still cause compile time error in the situation above.
Is there something I miss, or could we ignore the type on the right hand side if we do not care for clarity of code?
Thanks!
ArrayList<Integer> ali = new ArrayList();
and
ArrayList ali = new ArrayList<Integer>();
This will generate a compiler warning re: unchecked conversion. You will only get the compile safety of Generics if you do not ignore these warnings, or Supress them with the annotation because you can prove it's safe.
You do a raise an interesting point with:
ArrayList<Integer> ali = new ArrayList();
As you will only be using ali you do have safety with the reference. However you'll have the Compiler warning for the right hand side of the expression, so it's best to add the parameterized type and keep the compiler free of warnings. The reason the compiler is warning you is because someone could come and do this:
ArrayList<String> strings = new ArrayList<String>();
ArrayList<Integer> integers = new ArrayList(strings);
Oh no you've now got Strings in your Integers!
This is where Java 7's type inference comes in i.e.
ArrayList<Integer> ali = new ArrayList<>();
So there will no longer be a need for the parameterized type to be specified, as Integer is inferred. You can do this in Java 5 or 6 by writing a generic method such as makeArrayList() which infers the type (see Joshua Bloch Effective Java book)
You are right in your assessment that your last code snippet is not actually dangerous (though it does generate the compiler warning).
The reason why using raw types is potentially dangerous, is because you lose the type-safety that generics provides. More specifically, you lose the guarantee that you can't treat the " generic parameter as two different types in two different scenarios. (This is what the problem is in your casting-to-String example - the list is considered to contain integers at one point (when being populated) but considered to contain Strings at another point).
In the last example you've provided, the warning is technically spurious since the raw-typed list that's being constructed can only be referenced by the ali reference, which is correctly typed. Therefore, it would be impossible to insert strings into it.
However, the compiler can't guarantee this in general, as it's an implementation detail of how the ArrayList constructor works that makes this safe. (Another list implementation could "publish" a reference to itself externally, which could then be used to insert the wrong type of elements into this list). The compiler just sees that you're assigning something that's of the raw type ArrayList to a variable of type ArrayList<Integer>, and correctly says that "the thing on the right hand side might have been used for things other than Integers in the past, you know - are you sure this is OK?" It's roughly equivalent to
ArrayList al = new ArrayList();
ArrayList<Integer> ali = al;
where in this slightly expanded case, the "temporary" variable al allows one to call al.add("not an int") without compile time errors.
There's no real benefit to doing things this way and "knowing" it's correct, you may as well construct the list with the right generic parameters from the get-go, as in your first example. Unchecked conversion warnings are often not a real problem, but quite often can be - suppressing the warnings runs the risk that you'll migrate from the first situation to the second without noticing. Getting the compiler to check for you means it can tell you if your underlying assumptions become invalidated.
The compiler only checks if a variable that declares a generic type is used correct. If the variables type is a raw type, then it won't complain (with an error). So the following lines compile and run with no error:
ArrayList list = new ArrayList<Integer>();
list.add("hello");
String s = (String) list.get(0);
Note, that the compiler doesn't care that we used the generic constructor and that the runtime won't notice because of type erasure.
Once the variable has a generic type, then the compiler can check, if you use the variable "correctly" (like for collection: the compiler knows that get(0) will return the generic type and can complain on illgal casts)
The last example is safe (in this case). It is critical, if the constructor uses some typed parameters.
The following lines show the problem:
ArrayList<Double> doubles = new ArrayList<Double>();
ArrayList<Integer> integers1 = new ArrayList<Integer>(doubles); // error
ArrayList<Integer> integers2 = new ArrayList(doubles); // no error
With the third line we can legally populate a Integer typed array with Double values, we just have to ignore the warning (and catch all runtime exceptions later ;) )
OT and Trivia
With Java 7 we get the diamond operator:
ArrayList<List<Integer>> multilist = new ArrayList<List<Integer>>(); // Java 1.5+
ArrayList<List<Integer>> multilist = new ArrayList<>(); // Java 7+
The problem is that
ArrayList ali = new ArrayList<Integer>();
is an untyped Collection. The compiler warns your about it with this warning message:
ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized
However, since you have not typed it, the compiler can not know what types are in variable ali.
At runtime, however the type is erased - you in effect have ArrayList<Object>. When you retrieve an element (an Integer) and try to assign it to a String, it explodes, of course with a ClassCastException
If you don't care about code clarity, you could do it - but I don't see why you'd want to. There seems to be absolutely no gain (aside from saving a few characters worth of typing) in doing so, and it just makes your code slightly harder to read if you're not declaring and initialising on the same line.
By themselves, neither of these will produce runtime errors:
ArrayList ali = new ArrayList<Integer>();
or
ArrayList<Integer> ali = new ArrayList();
However, that's only because you've not tried to populate the list. If you do, and you make a mistake, you can get unexpected ClassCastExceptions when using values extracted from the list. For example:
ArrayList ali = new ArrayList();
ali.add("Hi mum");
ArrayList<Integer> oop = ali; // unsafe conversion
Integer first = oop.get(0);
The last line won't give a compilation error or warning, but at runtime it will give a ClassCastException. The CCE is thrown because the compiler does an implicit type cast as part of the assignment.

Categories

Resources