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.
Related
I'm new to Java, and I'm trying to figure out one thing about generics.
If I declare a method like
public <T> List<T> toList(final T... arr) { ... }
Can I return both ArrayList and LinkedList?
Or for example, if I have declare a method like
public <T> T[] toArray(final List<T> l) { ... }
Can I pass both ArrayList and LinkedList as argument and it'll works good?
If this is right, does it works with all objects too? So, if I create a class and I extend it more times, can I use the top class as arg of method, but the pass its subclasses when I call it?
It's not about generics only. You can assign any object to a variable of its parent class.
Object o = 5; // It's valid
If you pass a LinkedList or ArrayList to the toArray()method, it doesn't matter. It will be automatically converted to List. Similarly, if you return a LinkedList from the toArray() method, it doesn't matter. It will be converted to List.
But one thing to keep in mind is if you pass a LinkedList, it will get converted to List and you will be able to use only the methods of the List interface.
List list = new LinkedList();
list.addFirst(1); // Invalid
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;
}
So Google turns up a lot of questions about getting the .class of a parameterized type, but I'm trying to go the other way.
I have a list of Classes, and I need to make a map that uses the Class as a key, and an ArrayList of objects of type Class as the value. Something like this:
Class[] classes = getArrayOfClasses();
HashMap<Class, ArrayList<?>> map = new HashMap<Class, ArrayList<?>>();
for(Class c : classes) {
map.put(c, new ArrayList<c>()); // here is where the problem is
}
The problem of course is that it needs a parameterized type, not a class. One possible workaround is to just use map.put(c, new ArrayList<Object>()), but then I have to know the type and cast every object I pull.
MyClass myObj = (MyClass) map.get(MyClass.class).get(0);
I also tried making an initialization function like this:
private <T> ArrayList<T> makeArrayList(Class<T> c) {
return new ArrayList<T>();
}
This had the syntax that I hoped would work, but it still left me with an ArrayList of Object that had to be cast.
So is there a way I can just make the ArrayList parameterized with the Class's type?
Unfortunately, due to the nature of Java's generics (they work by erasure), generics aren't available at runtime.
This means that there is no real way to make map.put(c, new ArrayList<c>()); work.
I recommend doing this instead: Wrap your list in a dedicated object, and give that object the following accessor method:
public <T> getList(Class<T> key) {
List<?> list = map.get(key);
return (List<T>) list;
}
This is going to produce a warning, but as long as the map was constructed properly, you'll be ok.
Alternatively, you can do a run time check of all objects to make sure the type match up.
public <T> getList(Class<T> key) {
List<?> list = map.get(key);
for(Object o : list){
assert(key.isInstance(o));
}
return (List<T>) list;
}
There is absolutely no difference in the compiled bytecode between:
new ArrayList();
new ArrayList<T>();
new ArrayList<String>();
new ArrayList<Integer>();
The type parameter has no effect on the compiled code; it only affects the compiler's type-checking. So, if the type parameter is not available at compile-time, it is useless, as compile-time is the only time where it could be useful.
You should just write new ArrayList<Object>() or new ArrayList<Integer>() even new ArrayList<CompletelyBogusUnrelatedClass>(); it doesn't matter, because all of them are compatible with ArrayList<?>, which is the value type of your map.
As for your method that creates and returns an ArrayList, it should just be written like this:
private static <T> ArrayList<T> makeArrayList() {
return new ArrayList<T>();
}
(That's right, this method returns an ArrayList of whatever element type you want without even knowing what that type is! This is a clear demonstration that the type parameter is not needed at runtime.)
I'd like a convenience method to take a set of parameters and return an array, much like Arrays.asList(T... items) will take a set of parameters and return a List<T> of those items.
It's easy enough to write one, but does one already exist in java?
UPDATE
My bad! I didn't realize the question was so unclear. Your questions have forced me to realize that the question isn't quite the question I thought it was.
I have several calls like the following that place various key/values into a Map:
put( Key.get(A.class), new Key[] { Key.get(X.class), Key.get(Y.class), Key.get(Z.class)});
... where the map is of type Map<Key<? extends Foo>,Key<? extends Foo>[]>
I was looking for a typesafe and succinct way to execute the above statement, and I thought that something like the following would work:
put( Key.get(A.class), toArray( Key.get(X.class), Key.get(Y.class), Key.get(Z.class)));
... where toArray() is defined as something like
private static <T> T[] toArray( T... t ) {
return t;
}
However, it turns out that this solution is not typesafe itself, and thus it's really not much more succinct than just creating a new array manually using new. This was the first cause of my misunderstanding.
I thought that I could get typesafety by using a List instead of an array and then using Arrays.asList() to populate the values of the list, but it turns out that that's not typesafe either. This was the second cause of my misunderstanding. I thought that Arrays.asList() would make this statement more succinct than it actually does, and thus I was looking for something that would do the same for me for arrays.
So I suppose the question is really - Is there a succinct way to get typesafety in the above situation?
Arrays already have such a shortcut syntax:
String[] strArray = {"one", "two", "three"};
In response to your update:
As it seems like you discovered, arrays of parameterized types can never be type-safe. This is one of several limitations due to the fact that arrays and generics are like oil and water.
A varargs method such as Arrays.asList isn't spared from this limitation since varargs works by implicitly creating an array of the comma delimited arguments. In order to have type-safety, you'll need to avoid any solution involving arrays, including varargs.
First, I recommend you change your map's type to hold Lists instead of arrays:
Map<Key<? extends Foo>, List<Key<? extends Foo>>> map = new HashMap<>();
And then build a List before putting it in the Map:
List<Key<? extends Foo>> lst = new ArrayList<>();
lst.add(Key.get(X.class));
lst.add(Key.get(Y.class));
lst.add(Key.get(Z.class));
map.put(Key.get(A.class), lst);
If you want it all in one statement, it's going to be trickier without varargs. Guava's ImmutableList exposes the of factory methods taking up to 12 elements before falling back to varargs. If the Lists in the map aren't going to be modified later, you could store ImmutableList<Key<? extends Foo>> and use:
map.put(
Key.get(A.class),
ImmutableList.of(Key.get(X.class), Key.get(Y.class), Key.get(Z.class))
);
In fact you could still take advantage of those factory methods even if the List needs to be modifiable by copying the returned ImmutableList:
map.put(
Key.get(A.class),
Lists.newArrayList(ImmutableList.of(
Key.get(X.class),
Key.get(Y.class),
Key.get(Z.class)
))
);
But then you're introducing overhead just for the sake of style.
Side note: if you do happen to be using Guava, you might look at using a Multimap instead of a Map of Lists.
What would such a method do that the constructor for the array doesn't already?
String foo = "FOO";
String bar = "BAR";
String[] strings = new String[]{foo, bar};
How about
public static class ToArray {
public static <T> T[] toArray(T... items) {
return items;
}
}
public void example() {
String[] strings = ToArray.toArray("fred", "bob");
}
?
In order to get a Set and return a List you could use an ArrayList:
Set<String> set = new HashSet<String>();
set.add("str1");
set.add("str2");
set.add("str3");
List<String> list = new ArrayList<String>(set);
If you want an array from a list you could do something like:
myList.toArray();
// or even
String[] myStringArray = myList.toArray(new String[]);
Is this what you want? This will return an array because Java treats the varargs construct as an array. I don't know how to genericize it though.
public Object argsToArray(Object... args) {
return args;
}
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