Quick Question...
Can collections in Java hold more than one type? Or do they all have to be the same type?
thanks
Simple answer
Yes.
More detailed answer
You can either use generic collection, without <T> value, for example:
ArrayList a = new ArrayList();
a.add(2);
a.add("String");
Using collections without <T> is a bad habit and most IDEs / compilers give a warning here. You can circumvent it by using a collection of Object, i.e.:
ArrayList<Object> a = new ArrayList<Object>();
Or you can find some common interface or supertype that these element must have in, for example ArrayList<Number> - and you can store various objects that have common Number superclass, i.e. BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short:
ArrayList<Number> a = new ArrayList<Number>();
a.add(2); // integer
a.add(42L); // long
a.add(123.45d); // double
System.out.println(a.toString()); // => [2, 42, 123.45]
Note that it essentially means that a elements are of Number class — i.e. you can't ask to execute subclass-specific methods (for example, Double#isInfinite(), which doesn't exist in Number superclass), although you can typecast in run-time if you somehow know it's safe to typecast:
a.get(2).isInfinite() // compile-time error
((Double) a.get(2)).isInfinite() // => false
((Double) a.get(1)).isInfinite() // run-time error (ClassCastException)
Run-time typecasting is also generally frowned upon, as it effectively circumvents proper compile-time type safety.
Also note that it's impossible to assign (or use) ArrayList<Number> in place of ArrayList<Integer> and vice-versa, i.e. this will fail to compile:
public void printNumbers(ArrayList<Number> list) {
list.forEach(System.out::println);
}
ArrayList<Integer> a = new ArrayList<Integer>();
printNumbers(a); // "incompatible types"
as well as this:
public void printIntegers(ArrayList<Integer> list) {
list.forEach(System.out::println);
}
ArrayList<Number> a = new ArrayList<Number>();
printIntegers(a); // "incompatible types"
To declare a variable to be able to accept both ArrayList<Number> or any of its subclasses, one can use ArrayList<? extends Number> or ArrayList<? super Number> syntax. extends is generally used when you're going to consume (i.e. read) from the object in your method, super is used when you're going to produce (i.e. write). Given that printout is consuming, it's safe to use extends:
public void printNumbers(ArrayList<? extends Number> list) {
list.forEach(System.out::println);
}
ArrayList<Integer> listInt = new ArrayList<Integer>();
printNumbers(listInt); // works
ArrayList<Double> listDbl = new ArrayList<Double>();
printNumbers(listDbl); // also works
There is a good answer in
Difference between <? super T> and <? extends T> in Java for more in-depth explanation.
If you want them to hold any more than one type, use Collection<Object>. However, you won't know what you're getting without doing some if (x instanceof MyType) calls, which are rather inefficient.
They have to be of the same Supertype. So if you have objects of type A, then a Collection<A> can store objects of type A and of every subtype of A.
If you want to allow arbitrary types, then use Collection<Object>, otherwise take the most general appropriate super-class.
However, you will then have to manually cast from the most general type (Object) to the specific type you have in mind. You can use the typeof operator to find out what the type is.
Every Collection classes can contains heterogeneous objects except TreeSet and TreeMap. Since TreeSet and TreeMap stores elements according to some sorting order. so, if objects are of different type it will not be able to sort it because comparison between the objects will not be possible for sorting.
Yes they can but they should not (that's why generics have been put in place since 5th version of jdk) in general store different types, as this is the straight way to errors.
Yes collections in java can hold more than one type as below. But it will throw an exception if done using the following way.
ArrayList al = new ArrayList();
al.add(1);
al.add("name");
al.add(1.2f);
Iterator itr =al.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
Hence it's better to mention the type that you're using. To get rid of the exception the above program can be modified as below.
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(2);
al.add(3);
Iterator itr =al.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
ArrayList<String> al1 = new ArrayList<String>();
al1.add("Words");
al1.add("Names");
al1.add("Characters");
Iterator itr1 =al1.iterator();
while(itr1.hasNext())
{
System.out.println(itr1.next());
}
You can also use more than these types.
Yes,
My mistake the correct code is this one and
ArrayList<Elements>()=new ArrayList();
or
ArrayList<E>()=new ArrayList();
should be the correct declaration if you want to use Generics in Collection.
class Test
{
public static void main(String[] args)
{
// For Generic class of List
ArrayList<E> arrL1 = new ArrayList<E>();
arrL1.add("stackoverflow");
arrL1.add(1);
Iterator itr1=list.iterator();
while(itr1.hasNext())
{
System.out.println(itr1.next());
}
// for Particular datatype in List
ArrayList<String> list=new ArrayList<String>(); // Creating arraylist
list.add("Ravi"); // Adding object in arraylist
list.add("Vijay");
list.add("Ravi");
list.add("Ajay");
// transversing the values
Iterator itr=list.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
}
}
Output 1
stackoverflow
1
Output 2
Ravi
Vijay
Ravi
Ajay
I believe you can also use Collection<?>.
Yes, you can have more than one datatype in ArrayList of Collection.
class Test
{
public static void main(String[] args)
{
// For Generic class of List
ArrayList<> arrL1 = new ArrayList<>();
arrL1.add("stackoverflow");
arrL1.add(1);
// for Particular datatype in List
ArrayList<String> list=new ArrayList<String>(); // Creating arraylist
list.add("Ravi"); // Adding object in arraylist
list.add("Vijay");
list.add("Ravi");
list.add("Ajay");
// transversing the values
Iterator itr=list.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
}
}
Output 1:
stackoverflow
1
Output 2:
Ravi
Vijay
Ravi
Ajay
Related
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.
I am attempting to convert an ArrayList of class SomeClass to an ArrayList of class Object. This new ArrayList of Object will then be passed to a function. I currently have done the following:
// convert ArrayList<SomeClass> to generic ArrayList<Object>
Object[] objectArray = someClassList.toArray();
ArrayList<Object> objects = new ArrayList<Object>();
for (int i = 0; i < objectArray.length; i++) {
objects.add(objectArray[i]);
}
someFunction(objects);
public void someFunction(ArrayList<Object> objects) {
// do something with objects
}
Is there a more efficient or "standard" way of doing this? Is what I am doing "wrong" in the first place?
The purpose of converting it to ArrayList of class Object is that I have created an external library to process ArrayList of generic Objects.
If you are able to change the function's signature to taking an ArrayList<? extends Object> objects or an ArrayList<?> objects (or even better, List instead of ArrayList), you will be able to pass your ArrayList<SomeClass> directly.
The reason an ArrayList<SomeClass> is not an ArrayList<Object> is that an ArrayList<Object> would accept that you add() any kind of Object into it, which is not something you can do with an ArrayList<SomeClass>. On the other hand, an ArrayList<? extends Object> will allow you to retrieve elements from the list, but not add elements, so ArrayList<SomeClass> can safely be assigned to it.
Since you created the external library, I think it would be easier to modify the function signature to accept lists of any type. This can be accomplished using the unbounded wildcard ?:
public static void someFunction(List<?> objects) {
// whatever
}
Then you don't need to make any conversions to call it:
public static void main(String[] args) {
List<String> words = new ArrayList<>();
someFunction(words);
}
Also, unless you have a good reason not to, it would be better to accept any List in someFunction instead of limiting your input to ArrayLists. This makes your code more flexible and easier to change in the future.
A simple way to convert a List<SubFoo> to a List<Foo> is to use Collections.unmodifiableList(listOfSubFoos), which is perfectly type-safe and actually enforces that you can't do anything bad with it (like adding a DifferentSubFoo).
It is possible to transform the type parameters of a type in arbitrary ways with two casts:
ArrayList<SomeClass> l1 = ...;
ArrayList<Object> l2 = (ArrayList<Object>) (Object) l1;
But, as Aasmund Eldhuset also says in his answer: This is probably not a good idea! It is better to give a more suitable type to l2 instead, like ArrayList<?>.
This code gives you an compile warning saying Type safetyThat: Unchecked cast from Object to ArrayList<Object> for a reason. If for example a String is added to l2 and then someone reads l1 and expects a SomeClass they will get a very unexpected ClassCastException.
i have read on docs.oracle site that The following code snippet without generics requires casting:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
but if i write code with Generics then it is still prone to Error:
List<Object>= new List<Object>;
list.add("hello");
String s=(String)list.get(0);
what is then the real use of generics....:( thnx in advance..
List<Object>= new List<Object>;
list.add("hello");
String s=(String)list.get(0);
Should be
List<String>= new ArrayList<String>(); // this is now a list of String, not a list of object
^^^^^^ ^^^^^^
list.add("hello");
String s=list.get(0); // no casting needed
^
You parameterize by the type you want. Your example are 2 ways to do the same thing, since you parameterize by the most basic class.
The advantage of generics is that you can write classes that are more specific to one class, String here. This gives you better type safety to catch bugs early during compilation. This prevents issues arising from the casting approach.
Using generics makes your code Type Safe. You can prevent ClassCastException.
Suppose you want to store a list of names(string)
List listNames = new ArrayList();
listNames.add("Durgesh");//ok
But I could also add an integer to it
listNames.add(5000);//storing int instead of string
Now do this
String name2=listNames.get(1);//throws exception{int->string}
Without generics you could add invalid types to collection which could break your code.
With generics you could solve the problem
List<String> listNames = new ArrayList();
listNames.add("Durgesh");
listNames.add(3000);//would through error at compile time
So,generics provides typesafety
With List<Object> you intend to add any kind of Object.Due to Object parameter,it would allow you to add any kind of object(string,int).
Also List<x> cannot be assinged(=) to List<y> or vice versa if x can be converted to y or y can be converted to x..They both should be x or y thus providing type safety
So,you wont be able to assign(=) List<String> to List<Object> or vice versa..
Generics are used to detect runtime exceptions at compile-time itself.
Assume that you created a List to store Strings and passed it to a method.. enhanceList(List).. and after the execution, you will iterate through the list and get all strings
before genercis, it could have been possible that enhanceList(List) method will add other type of objects into the list creating possible ClassCastException
void someMethod() {
List listOfStrings = new List();
enhanceList(listOfStrings);
for(Iterator i : listOfStrings.iterator(); i.hasNext();) {
String s = (String) i.next(); //RuntimeException here
}
}
void enhanceList(List l) {
l.add(new Integer(1)); //error code
}
with generics, you can very well "bind" the type of objects the list contains
void someMethod() {
List<String> listOfStrings = new List<String>();
enhanceList(listOfStrings);
for(String s : listOfStrings) {
//no error here
}
}
void enhanceList(List<String> l) {
l.add(new Integer(1)); //compile-time error
}
However, generics should be used with caution, List<Object> doesn't help much with binding types because, it can hold any objects (since Object is super class of all the java classes). I recommend to create List of Specific type always.
Why can't I create an array of List ?
List<String>[] nav = new List<String>[] { new ArrayList<String>() };
Eclipse says "Cannot create a generic array of List"
or
ArrayList<String>[] nav = new ArrayList<String>[] { new ArrayList<String>() };
Eclipse says "Cannot create a generic array of ArrayList"
or
List<String>[] getListsOfStrings() {
List<String> groupA = new ArrayList<String>();
List<String> groupB = new ArrayList<String>();
return new List<String>[] { groupA, groupB };
}
But I can do this:
List[] getLists() {
return new List[] { new ArrayList(), new ArrayList() };
}
Eclipse says that List and ArrayList are raw types but it compiles...
Seems pretty simple, why won't it work?
Well, generics tutorial give the answer to your question.
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:
// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;
// Run-time error: ClassCastException.
String s = lsa[1].get(0);
If arrays of parameterized type were
allowed, the previous example would
compile without any unchecked
warnings, and yet fail at run-time.
We've had type-safety as a primary
design goal of generics.
You can't create arrays of generic types, generally.
The reason is that the JVM has no way to check that only the right objects are put into it (with ArrayStoreExceptions), since the difference between List<String> and List<Integer> are nonexistent at runtime.
Of course, you can trick the compiler by using the raw type List or the unbound wildcard type List<?>, and then cast it (with a unchecked cast) to List<String>. But then it is your responsibility to put only List<String> in it and no other lists.
No exact answer, but a tip:
Last example has a raw type warning because you omitted the typization of the list; it is generally a better (type safe) approach to specify which object types are contained in the list, which you already did in the previous examples (List<String> instead of List).
Using arrays is not best practice, since their use contains errors most times; Using Collection classes (List, Set, Map,...) enables use of typization and of convenient methods for handling their content; just take a look at the static methods of the Collections class.
Thus, just use the example of the previous answer.
Another solution is to extend LinkedList<String> (or ArrayList<String>, etc.), then create an array of the subclass.
private static class StringList extends LinkedList<String> {}
public static void main(String[] args)
{
StringList[] strings = new StringList[2];
strings[0] = new StringList();
strings[1] = new StringList();
strings[0].add("Test 1");
strings[0].add("Test 2");
strings[1].add("Test 3");
strings[1].add("Test 4");
System.out.println(strings[0]);
System.out.println(strings[1]);
}
I have a method that takes a list of SResource objects
public static List<STriple> listTriples(List<SResource> subjects){
//... do stuff
}
Why can't I do this
List<IndexResource> resultsAsList = new ArrayList<IndexResource>();
resultsAsList.addAll(allResults.keySet()); // I could possible not use lists and just use sets and therefore get rid of this line, but that is a different issue
List<STriple> triples = new ArrayList<STriple>();
triples = TriplesDao.listTriples(resultsAsList);
(The compiler tells me I have to make triples use SResource objects.)
When IndexResource is a subclass of SResource
public class IndexResource extends SResource{
// .... class code here
}
I would have thought this has to be possible, so maybe I am doing something else wrong. I can post more code if you suggest it.
You can do it, using wildcards:
public static List<STriple> listTriples(List<? extends SResource> subjects){
//... do stuff
}
The new declaration uses a bounded wildcard, which says that the generic parameter will be either an SResource, or a type that extends it.
In exchange for accepting the List<> this way, "do stuff" can't include inserting into subjects. If you're just reading from the subjects in the method, then this change should get you the results you want.
EDIT: To see why wildcards are needed, consider this (illegal in Java) code:
List<String> strings = new ArrayList<String>();
List<Object> objList = string; // Not actually legal, even though string "is an" object
objList.add(new Integer(3)); // Oh no! We've put an Integer into an ArrayList<String>!
That's obviously not typesafe. With wilcards, though, you can do this:
List<String> strings = new ArrayList<String>();
string.add("Hello");
List<? extends Object> objList = strings; // Works!
objList.add(new Integer(3)); // Compile-time error due to the wildcard restriction
You can't do this because generics are not "covariant" i.e. a List<Integer> is not a sub-class of the List<Number> though Integer is a sub-class of Number.
For those who are unable to add wildcards, this should work.
List<Integer> list = new ArrayList<Integer>();
new ArrayList<Number>(list);