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
Related
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 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.
I'd just started working with lists and wanted to know how both of these are different?
Im guessing the second one specifically points out that it contains only string type stuff, while the first one is more flexible.
But then if the first is more flexible, why do people use the second one ever?
It's called generics.
The second one specifies this is a list of Strings and will throw a compiler error if you try to put something else.
It is useful to prevent people from putting anything besides a String in the List.
Here is a link to the java generic tutorial.
List<E> : Its generic and E will be any object i.e it will contain List of Object.
List<String> : The data type of list is string i.e it can only contain String.
It depend on the requirement what is the need of datatype of List.May be whatever example you looked require list of String.
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)
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.