Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collection in Java

Tags:

java

Quick Question... Can collections in Java hold more than one type? Or do they all have to be the same type?

thanks

like image 957
Jake Avatar asked Sep 10 '26 03:09

Jake


2 Answers

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.

like image 165
GreyCat Avatar answered Sep 12 '26 16:09

GreyCat


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.

like image 39
fmt Avatar answered Sep 12 '26 17:09

fmt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!