I have 3 arrays, 2 arrays of the type int, and one array of the type String.
My question is, is it possible to make a List that contains those 3 arrays?
I want to be able to do something like this: myList.get(0) which should give me the first array in the List.
That is possible but which value to add arrays of different types in a List ?
You will have to declare a List<Object> or List<Object[]>. So you lose the benefit of type safety as you have to cast the array or the array elements to manipulate other things as Object variables.
For example :
List<Object[]> list = new ArrayList<>();
list.add(new String[5]);
list.add(new Integer[5]);
Object[] objects = list.get(0);
You is stuck with an Object[].
To manipulate a more specific type you will have to perform downcast :
String[] objects = (String[]) list.get(0);
This will work but you can make an error such as :
Integer[] objects = (Integer[]) list.get(0);
And you would have the information only as an exception at runtime.
Note that using the instanceof operator before downcasting the array will not prevent malfunctioning of the program.
For example this will not rise a ClassCastException as the conditional statement will be evaluated to false :
if (list.get(0) instanceof Integer[]){
Integer[] objects = (Integer[]) list.get(0);
...
}
But as consequence, it will never execute the processing. It is in a some way even worse as the issue could be not visible for the client.
And using a reversed logic will throw an exception as the initial code if the client uses incorrectly the list but is finally more verbose :
if (!(list.get(0) instanceof Integer[])){
... // throw exception
}
Integer[] objects = (Integer[]) list.get(0);
So definitely, you should forget the idea to add these arrays in a List.
Related
I was working on Collection framework in Java , where i encountered a strange problem .
I made 2 lists of Strings 1 with the help of ArrayList while second was made using Arrays.asList(T ...).
After creation of these two list i tried to convert these lists into String arrays with the list.toArray() ,
as list.toArray() method call returns an object array , so i had to explicitly cast to String[] .
After casting some strange behaviour is happening as :
Case #1 : ( where list was created using ArrayList) , gives runtime exception as
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
Case 2 : (where list as created using Arrays.asList(T ... ) ) runs fine .
here is the code
String [] str = null ,str1 = null ;
List<String> list = new ArrayList<String>();
list.add("a");
List<String> list1 = Arrays.asList("a");
str = (String[]) list.toArray(); // Runtime Exception
str1 = (String[]) list1.toArray(); // Runs Fine
An ArrayList is backed by an Object[]. A copy of that array is returned with toArray().
Returns an array containing all of the elements in this list in proper
sequence (from first to last element).
It make no guarantees on the type of array returned. But we know this from the exception's message. If you want it to return a String[], use the overloaded method provided for this reason.
str = list.toArray(new String[0]);
The cast becomes unnecessary.
The List implementation returned by Arrays.asList maintains a reference to the array (implicit or explicit) passed as its variable arity argument.
Returns a fixed-size list backed by the specified array.
The invocation
List<String> list1 = Arrays.asList("a");
creates a String[] and passes that as the argument to asList. This isn't specified clearly by the API documention, but backed by seems to indicate that it will return that same array. (Looking at the implementation, it returns a clone.) Since it is a String[], there is no error when casting and assigning it to a variable of that type.
In both cases, the appropriate solution is to use the overloaded List#toArray(T[]).
For fun, run the following and check the type of array that is returned.
List<String> list1 = (List) Arrays.<Object> asList("a");
System.out.println(list1.toArray().getClass());
Don't make assumptions. Always rely on the API documentation. If it isn't clear, try to find a better solution.
The different calls to toArray are returning arrays with different component types. You can see this by running the following code:
List<String> list = new ArrayList<String>();
list.add("a");
List<String> list1 = Arrays.asList("a");
System.out.println(list.toArray().getClass());
System.out.println(list1.toArray().getClass());
On any version of Java 8 or earlier, the result is
class [Ljava.lang.Object;
class [Ljava.lang.String;
Basically this output means Object[] and String[] respectively.
However, this is a bug. See JDK-6260652. Although it's not stated very clearly, Collection.toArray() must return an Object[] and not an array of some subtype of Object. There are a couple reasons for this.
First is that Collection.toArray() was introduced in JDK 1.2, long before generics were added to the language. There was no possibility of any collection implementation returning anything other than Object[], so for compatibility, all collections' toArray() implementations must return Object[].
The second reason is that a rather offhand comment in the specification for toArray(T[]) says:
Note that toArray(new Object[0]) is identical in function to toArray().
which again requires toArray() to return Object[] and not an array of some other type.
This bug has been fixed in JDK 9. Running the code snippet above on a recent JDK 9 build gives the following output:
class [Ljava.lang.Object;
class [Ljava.lang.Object;
The fact that Arrays.asList("a") uses a String[] for internal storage is an implementation detail. The bug where toArray() returned something other than Object[] is this implementation detail leaking out. (In fact, the array is created by the varargs machinery, using the method's type parameter as the array component type. Arrays.asList() just wraps the array it's given.)
As others have said, if you want to control the component type of the returned array, use the other overload toArray(T[]):
String[] array = list.toArray(new String[0]);
String[] array1 = list1.toArray(new String[0]);
List<String> list = new ArrayList<String>();
list.add("a");
str = (String[]) list.toArray();
In this case your list invoke method toArray() of ArrayList class. Which looks like below, it returns Object []:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
And elementData declare:
transient Object[] elementData;
And constructor method:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
And DEFAULTCAPACITY_EMPTY_ELEMENTDATA:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
There for, elementData is totaly Object [] and can't be casted to any type, String etc...
With Arrays.asList(T...), it returns java.util.Arrays$ArrayList class. And java.util.Arrays$ArrayList also has toArray() method. That subtle toArray() method makes some confuse :). Here is its implementation:
public Object[] toArray() {
return a.clone();
}
And finally a field declare:
private final E[] a;
java.util.Arrays$ArrayList.toArray() able to return Object [] and actually E []. Hope this will help you :)
The key here is that Arrays.asList(..) does not return a java.util.ArrayList, but instead it returns a java.util.Arrays$ArrayList. So the .toArray() methods vary slightly.
If you want the first case to return a String[], you can change the call to
str = list.toArray(new String[0]);
When I run my code I get the following error:
Caused by: java.lang.ClassCastException: java.util.Collections$UnmodifiableSet cannot be cast to [Ljava.lang.Integer;
The code that causes is:
Integer[] selects = (Integer[]) tbl_analytes.getValue();
But when I do this:
Object obj = tbl_analytes.getValue();
System.out.println(obj);
I get the following output:
[1,7,15]
I don't understand why I can't convert this to an Integer[]. Any tips?
The Collection interface has methods to convert Collections to arrays :
Object[] toArray();
or
<T> T[] toArray(T[] a);
You can't just cast an object one one type to an unrelated type and expect it to work.
The fact that printing the Set produces an output that looks like the output you get when printing an array doesn't mean anything.
final ArrayList<Integer> integerList = new ArrayList<>();
final Integer[] integerArray = new Integer[integerList.size()];
integerList.toArray(integerArray);
It seems that 'tbl_analytes' is a Set, not an array. You can not cast it, you have to convert it:
Integer[] array = set.toArray(new Integer[0]);
You have to use the toArray(T a[])-method defined in the Collection interface, passing an empty Integer[] to the method. This solution assumes, that your List is of type List<Integer>. Otherwise, this would fail.
Set<Integer> values = (Set<Integer>) tbl_analytes.getValue();
The class cast exception gives the actual type. So use that or better a generalisation (List instead of ArrayList etcetera).
In the java language arrays are primitive. Collections are not.
Since a set is unordered it has no way to understand how to build an ordered array from a hashed set of memory locations.
When you output a set you are calling the toString method on that set.
So:
When I call
Integer[] selects = (Integer[]) tbl_analytes.getValue();
I get the following error:
Caused by: java.lang.ClassCastException: java.util.Collections$UnmodifiableSet cannot be cast to [Ljava.lang.Integer;
But when I do this:
Object obj = tbl_analytes.getValue();
System.out.println(obj);
I get the following output
[1,7,15]
The reason is Integer[] is completely uncastable from Set<Integer> but
the output from toString is derived from:
The java.util.Collections$UnmodifiableSet extends java.util.AbstractCollection
https://docs.oracle.com/javase/10/docs/api/java/util/AbstractCollection.html#toString()
Returns a string representation of this collection. The string representation consists of a list of the collection's elements in the order they are returned by its iterator, enclosed in square brackets ("[]"). Adjacent elements are separated by the characters ", " (comma and space). Elements are converted to strings as by String.valueOf(Object).
That is the resason you are getting the exception. The solution to it is answered here in this stack overflow question:
How to convert Set<String> to String[]?
I have a function that return a raw vector. I know that all the elements in the vector are string but the code stays for leagacy reasons. I want to get a arraylist from this data.
One naive way is to iterate the vector and add elements to the list. Is there any short way of doing it which can prevent looping. Or may be a direct function which enables this.
Edit:
Example:
Vector f1() {} //f1 returns raw vector
I want to achieve the following:
List<String> l = new ArrayList<String>();
Vector vec = f1();
for(Object obj: vec) {
l.add((String) obj);
}
Note: I have not checked if the above code compiles. Please treat it as a pseudo code
If you are 100% sure the Vector only contains Strings, the simplest way is:
List<String> list = new ArrayList<>(vector);
Note that this will compile and run fine, even if you Vector contains other types of objects. However this:
list.get(i);
will throw a ClassCastException if the i-th element was not a String.
Since you have a raw Vector you will get warnings. If you want to get rid of them you can use:
#SuppressWarnings(value = {"unchecked", "rawtypes"})
public static List<String> rawVectorToList(Vector v) {
return new ArrayList<>(v);
}
An alternative to detect casting issues fast is to copy the array manually (what the copy constructor does under the hood):
Vector v = ...;
String[] elements = Arrays.copyOf(v.toArray(), v.size(), String[].class);
List<String> list = Arrays.asList(elements);
or if you need the list to be mutable:
List<String> list = new ArrayList<> (Arrays.asList(elements));
This has the benefit of checking the type at copy time.
I am preparing for the entry-level Oracle certification - OCA - Java Programmer I, since they require people to take this one before taking the next one (used to be possible to just go for SCJP directly, which is equivalent of OCP - Java Programmer II)
I came across this question on array initialization, that got me a bit puzzled. Obviously, one can declare and initialize an array like this:
Object[] objects = { new Object[1], new Object[34] };
as the arrays are objects, you can stick object arrays into an object array. You can easily get at one or the other object array by doing objects[0] or objects[1] but where would you go from there? How would you access the, say, 16th Object from the object array stored under objects[1]?
Basically, my question can be simplified to this:
Object o = new Object[100];
The above compiles. However, how would one access individual objects in the Object array o?
An Object[] is also an Object, which is why your declaration
Object o = new Object[100];
works.* To access the elements, though, you need to cast it back to an Object[]. For example:
Object elt = ((Object[]) o)[3];
For your original declaration:
Object[] objects = { new Object[1], new Object[34] };
you will have to do a similar thing to access the 16th element of objects[1]:
Object elt = ((Object[]) (objects[1]))[15];
Of course, you can avoid all this casting by declaring:
Object[][] objects = { new Object[1], new Object[34] };
in the first place. Then you can just do objects[1][15].
* Note that this is true only of Object, which has special status as the root of the object hierarchy in Java. An Integer[] cannot be assigned to an Integer variable.
You'd have to cast it back to Object[]:
Object o = new Object[] {new String("abc"), null, new Integer(1)};
Object[] arr = (Object[]) o;
Object elem = arr[0];
System.out.println(elem);
This prints abc.
It works because System.out.println() is happy to take an Object. If it required a String, you'd need another downcast.
List<String> v = new ArrayList<String>();
I understand the generics helps you declare that the ArrayList() has Objects of type String. My question is
how is the following one different from the above?
List<String> v = new ArrayList();
or the one below different from others
List v = new ArrayList<String>();
List<String> v = new ArrayList();
This one isn't really functionally different. The type parameter on the right side doesn't really do anything. It's used as a matter of style and to avoid the use of a Raw type, which is considered a programming error. In fact, in Java 7 it has been enhanced so you can just do this: List<String> v = new ArrayList<>(); and not have to repeat yourself on the right hand side.
List v = new ArrayList<String>();
The list with no type parameter is called a Raw Type. It is generally considered a programming error to declare a Raw Type in new code that is using generics. Basically there is no type checking going on at all when you declare it this way, you can put anything in that list.
Java generics are a compile time check. So it is the type of the reference at compile time that matters. If your reference is of type Raw List it doesn't matter what you declared on the right hand side, that is what the compiler will check against.
List<String> isn't really a "List that has Strings." It is a "List that I have asked the compiler to return errors and/or warn me if I put something in there that isn't a String. If you ignore compiler warnings, it is perfectly possible to get stuff in there that isn't a String.
List v = new ArrayList();
It is before java 5 way of declaring a list.
List<String> v = new ArrayList<String>();
It uses generics, introduced in java 5. It adds compile-time type safety.
List<String> v = new ArrayList<>();
is just an optimization introduced in java 7. It just simplifies the code
by maintaining the type safety
public static void main(String[] args) throws Exception {
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList(); //This is equivalent to list1 but with compilation warning
List list3 = new ArrayList<Integer>(); //This is equivalent to list3 = new ArrayList<Object>()
//list1.add(new Object()); //does not compile
//list2.add(new Object()); //does not compile
list3.add(new Object()); //this is fine
list1 = list3; //ok, but
System.out.println(list3.get(0)); // this is fine
System.out.println(list1.get(0)); //Runtime error: ClassCastException
//List<Object> list5 = list1; //does not compile
List<Object> list5 = list3; //ok
}
These all compile, and are all valid. The second case is likely only going to throw a warning - you won't be able to add anything that isn't a string due to List<String>. In the third case you have the opposite problem. You CAN add things that aren't Strings, but may run into a runtime exception as a result.
In the first and third case (new ArrayList<String>()) you instantiate an ArrayList that can hold String instances
In the second case (new ArrayList()) you instantiate an ArrayList that can hold Object instances (that is instances of any type -- even mix & match)
In the first and second case (ArrayList<String> v) you declare an instance of ArrayList that can hold String instances
In the third case (ArrayList v) you declare an instance of ArrayList that can hold instances of Object.
The problem with the second case is that if you were to get hold of a "raw" ArrayList (like the one instantiated), then it could hold anything theoretically; not just String instances, which is what the users of the declaration expect.
Similarly, in the third case, you create an ArrayList that is supposed to hold Strings, but the users of the declaration do not know that and might try to put other object instances in it
Note: of course under the hood, in the JVM, the generic type information is lost, so there are no differences in terms of execution, but for programming type safety, the compiler will flag the incorrect use. This way, there is no need to dynamically check/ensure the type of the objects put into/coming out of the list -- you can assume they are the correct type, because the compiler ensured this