Just for review, can someone quickly explain what prevents this from working (on compile):
private HashSet data;
...
public DataObject[] getDataObjects( )
{
return (DataObject[]) data.toArray();
}
...and what makes this the way that DOES work:
public DataObject[] getDataObjects( )
{
return (DataObject[]) data.toArray( new DataObject[ Data.size() ] );
}
I'm not clear on the mechanism at work with casting (or whatever it is) that makes this so.
Because toArray() creates an array of Object, and you can't make Object[] into DataObject[] just by casting it. toArray(DataObject[]) creates an array of DataObject.
And yes, it is a shortcoming of the Collections class and the way Generics were shoehorned into Java. You'd expect that Collection<E>.toArray() could return an array of E, but it doesn't.
Interesting thing about the toArray(DataObject[]) call: you don't have to make the "a" array big enough, so you can call it with toArray(new DataObject[0]) if you like.
Calling it like toArray(new DateObject[0]) is actually better if you use .length later to get the array length. if the initial length was large and the same array object you passed was returned then you may face NullPointerExceptions later
I asked a question earlier about Java generics, and was pointed to this FAQ that was very helpful: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
To ensure type safety when casting an array like you intended (DataObject[] dataArray = (DataObject[]) objectArray;), the JVM would have to inspect every single object in the array, so it's not actually a simple operation like a type cast. I think that's why you have to pass the array instance, which the toArray() operation then fills.
After Java 8 with introduction of streams and Lambda you can do the following too:
For casting a normal Array of objects
Stream.of(dataArray).toArray(DataObject[]::new);
For casting a List
dataList.stream().toArray(DataObject[]::new);
Related
public T[][] getArrayOfBlocks() {
Node node = this.first;
#SuppressWarnings("unchecked")
T[][] result = (T[][]) new Object[this.nNodes][this.arraySize];
for(int i = 0; i < this.nNodes; i++)
{
for(int j = 0; j < this.arraySize; j++)
if(node.a[j] != null)
result[i][j] = node.a[j];
node = node.next;
}
return result;
}
(Im a newbie in java so my wording will be a bit weird)
Im trying to make a method that creates a 2d array out of a T type unrolled linked list. When i test out the method above using the Integer class instead of the T type i get an error that says
Exception in thread "main" java.lang.ClassCastException: class [[Ljava.lang.Object; cannot be cast to class [[Ljava.lang.Integer; ([[Ljava.lang.Object; and [[Ljava.lang.Integer; are in module java.basa of loader 'bootstrap')
So yeah i would like to know if theres any way to solve this error without changing the return type.
Thanks in advance :)
y out of a T type unrolled linked list.
This is impossible.
Generics are a figment of the compiler's imagination. Generics completely disappear once your java code has been turned into a class file, or if they don't (generics in signatures), the JVM treats it as a comment. It has absolutely no effect. Instead, the compiler uses it to generate errors or warnings and inject invisible casts. This:
List<String> x = new ArrayList<String>();
x.add("Hello");
String y = x.get(0);
ends up in class code as indistinguishable from compiling:
List x = new ArrayList();
x.add("Hello");
String y = (String) x.get(0);
Try it if you're having a hard time getting your head around this idea. Write both, compile it, run javap -c -v to see the bytecode. Identical.
The reason x.add(5) would not work as replacement for x.add("Hello") is simply because javac won't let it happen. If you hack javac to allow it, you get a class file just fine and it verifies just fine. The x.add(5) will even execute just fine. You'd get a ClassCastException on the next line, simply because you're casting an instance of Integer to String.
As a consequence, there is no way to tell the difference between a new ArrayList<String>(); and a new ArrayList<Integer>() at runtime. Obviously; generics disappears; those are both just new ArrayList(), that's it.
In contrast, arrays are 'reified': They aren't a figment of javac's imagination. You can actually get this stuff at runtime. There is a difference between new String[0] and new Integer[0]:
Object[] arr1 = new String[0];
System.out.println(arr1.getClass().getComponentType()); // prints 'String'
It is impossible to write the identical code for generics:
List<?> list1 = new ArrayList<String>();
System.out.println(--nothing you can write here will print String--);
Hence, in your 'unrolled code with T', T is not something you can translate to an actual runtime type, and that means it is impossible to make an array of T.
Still having a hard time believing this? Peruse the API of java.util.List, specifically the various toArray methods it contains.
Look at the no-args one: toArray(). There are two explanations here:
The designer of this class was an utter idiot, because that returns Object[], which is stupid, because clearly that should return T[].
Or, perhaps something else is going on and they 'know' that it is in fact impossible to return a T[] there.
It's, as the rest of this post hopefully already suggested, the second reason.
Fortunately, there are 2 other toArray methods and those two do both return T[] as you desire. They are both based around the notion that the caller puts in the effort of providing that T type for you.
The first version is toArray(T[] in). The toArray code will use the provided array if it is large enough, but if not, it just makes a new one that is the right size and returns it. In practice, you always call listOfStrings.toArray(new String[0]) (you may think new String[list.size()] would be faster - no, that is slower1. A nice example of why writing more complex code because it seems faster is a bad idea. JVMs are far too complex to predict performance like this).
The trick here is that the code in list's toArray will take that array, grab its class (tossing the created array aside), get the component type from that, and then use that to make a new array.
There is another one, too: toArray(IntFunction<T[]> arrayCreator) (you need to look at the javadoc of Collection to see it; it is inherited).
Here we ask the caller to provide code that makes a new array. You use it like this: listOfStrings.toArray(String[]::new).
Pick your poison, or add both. Either trick will work here:
public T[][] getArrayOfBlocks(T[] dummy) {
Class<?> componentType = dummy.getClass().getComponentType();
#SuppressWarnings("unchecked")
T[][] arr = (T[][]) java.lang.reflect.Array.newInstance(componentType, this.nNodes, this.arraySize);
.. code continues here ..
}
or:
public T[][] getArrayOfBlocks(BiFunction<Integer, Integer, T[][]> arrayMaker) {
T[][] arr = arrayMaker.apply(this.nNodes, this.arraySize);
.. code continues here ..
}
Yes, they are both annoying. There are other options but they have significant downsides - the above 2 options are your best bet. That or forget about arrays. Why do you even want a `T[][]` in the first place? Arrays can't grow or shrink, assuming it's not a primitive array (and this isn't, by definition; generics cannot be primitive) they are not more performant, and their toString/equals/hashCode implementations are surprising (that's programmer-ese for 'basically broken'). Their API is non-existent. Why would you want to offer it?
1) In case you desire explanations for this one: It's because the toArray code is hotspot intrinsiced and knows it doesn't need to wipe out the memory space, whereas with `new String[100]`, those 100 references all need to be nulled out first because java guarantees you can't 'see' uninitialized memory.
I am just curious about the Lists class implementation of google guava, we have two methods to create List from Array,
Lists.asList(E first, E[] rest)
Lists.asList(E first, E second, E[] rest)
Why do these methods has first and rest separately? cant it be like Arrays.asList implementation of java ?
The only thing I am able to see is the first and second is nullable and rest is not nullable
Can anyone help to understand this ?
Ok, so the job of the Lists.asList() is not exactly to directly convert an array to a list.
Suppose we have an array, and we want to insert an element to it, we can’t do it as the array size is fixed. One solution to this problem is to allocate an new array of +1 the size of the original array and copy all elements from the original array to the new array. This works but it is highly inefficient.
Guava provides an efficent solution to this problem – Guava’s Lists.asList() method returns an unmodifiable list backed by the original array which also contains the specified element.
source: https://www.techiedelight.com/guava-lists-class-java/
So basically, you can use it to just convert an array by giving the first and/or second parameters (depending upon what method you are using) as null, and giving the "rest" parameter as your array.
This will return your array as list, perhaps with null as the first index (and second as well, depending on what you are using)
But if you want, you can use the same methods to get a list with some specific data appended to your array (at first and second index values)
Hope this was helpful!
The main reason these methods exist is to help you when you write a method with a signature like
void foo(Bar first, Bar... rest);
which is something you'd do when you want to allow the user to call the method as if it were a method with just a varargs parameter, but you want to require that it be called with a minimum of one or two arguments (whereas varargs alone would allow them to call it with zero). In the implementation of such a method, you might want to treat those arguments as a single list. Lists.asList does that without doing any array copying:
void foo(Bar first, Bar... rest) {
List<Bar> bars = Lists.asList(first, rest);
// ...
}
I faced a problem yesterday, when I was writing my homework. I finished the homework, but I still don't really understand why my code works. I had to write a sort function that takes an varargs of any comparable generic object as an argument and return the argument. The problem was that I had to return an array of sorted objects. So I had to learn more about varargs lists and arrays.
The function was defined like this.
public <T extends Comparable<T>> T[] stableSort(T ... items)
and inside the function I made a list, which I would sort and do all the work on.
List<T> list = new ArrayList<T>(Arrays.asList(items));
and at the end of the function I was returning list toArray so that it matched the output type T[].
list.toArray(items.clone());
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function. That seemed like doing two same things to me. I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray(). I know that this was the correct way to write it, because I finished the homework yesterday and found out this way from forums of the class, but I still don't understand why.
EDIT
The task required me to create a new array with sorted files and return it instead. Due to Type Erasure, it is not possible to instantiate an array of a generic type without a reference to a class that fits the generic. However, the varargs array has type T, so I should have cloned an array of a type which fits the generic constraints. Which I didn't know how to do in time. So I decided to use list to make my time easier till the deadline.
My question is since I already made the list from the varargs, why do I have to do items.clone()
You are right. Unfortunately, the compiler will be unable to determine the type of the array if you simply use the toArray() method. You should get a compilation error saying Cannot convert from Object[] to T[]. The call to item.clone() is required to assist the compiler in type-inference. An alternate approach would be to say return (T[])list.toArray
That said, I would not recommend either of the approaches. It doesn't really make sense to convert an array to a list and convert it back to an array in the first place. I don't see any significant take-aways that you would even understand from this code.
It seems to me there are a few questions here, that may have come together to create some confusion as to why what needs to be done.
I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray().
This is probably just the way it is typed, but it should be made clear that you don't clone the objects in the array, but only make a new List with the references to the objects in the array. The objects themselves will be the same ones in the array as in the List. I believe that is probably what you meant, but terminology can be tricky here.
I thought arrays.asList() would clone the values of array to list...
Not really. Using Arrays.asList(T[] items) will provide a view onto the array items that implements the java.util.List interface. This is a fixed-size list. You can't add to it. Changes to it, such as replacing an element or sorting in-place, will pass through to the underlying array. So if you do this
List<T> l = Arrays.asList(T[] items);
l.set(0, null);
... you've just set the element at index 0 of the actual array items to null.
The part of your code where you do this
List<T> list = new ArrayList<T>(Arrays.asList(items));
could be written as this:
List<T> temp = Arrays.asList(items);
List<T> list = new ArrayList<T>(temp);
The first line is the "view", the second line will effectively create a new java.util.ArrayList and fill it with the values of the view in the order they are returned in by their iterator (which is just the order in the array). So any changes to list that you make now don't change array items, but keep in mind that it's still just a list of references. items and list are referencing the same objects, just with their own order.
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function.
There could be two reasons here. The first is as CKing said in his/her answer. Because of type erasure and the way arrays are implemented in Java (there are separate array types depending on whether it's an array of primitives or references) the JVM would not know what type of array to create if you just called toArray() on the list, which is why that method has a return type of Object[]. So in order to get an array of a specific type, you must provide an array to the method that can be used at run-time to determine the type from. This is a piece of the Java API where the fact that generics work via type-erasure, aren't retained at run-time and the particular way in which arrays work all come together to surprise the developer. A bit of abstraction is leaking there.
But there might be a second reason. If you go check the toArray(T[] a) method in the Java API, you'll notice this part:
If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.
Suppose some code by another dev is using your stableSort method like this:
T[] items;
// items is created and filled...
T[] sortedItems = stableSort(items);
If you didn't do the clone, what would happen in your code would be this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
// List is now a new ArrayList with the same elements as items
// Do some things with list, such as sorting
T[] result = list.toArray(items);
// Seeing how the list would fit in items, since it has the same number of elements,
// result IS in fact items
So now the caller of your code gets sortedItems back, but that array is the same array as the one he passed in, namely items. You see, varargs are nothing more than syntactic sugar for a method with an array argument, and are implemented as such. Perhaps the caller didn't expect the array he passed in as an argument to be changed, and might still need the array with the original order. Doing a clone first will avoid that and makes the effect of the method less surprising. Good documentation on your methods is crucial in situations like this.
It's possible that code testing your assignment's implementation wants a different array back, and it's an actual acquirement that your method adheres to that contract.
EDIT:
Actually, your code could be much simpler. You'll achieve the same with:
T[] copy = items.clone();
Arrays.sort(copy);
return copy;
But your assignment might have been to actually implement a sorting algorithm yourself, so this point may be moot.
You need to use this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
when you want to do an inline declaration.
For example:
List<String> list = new ArrayList<String>(Arrays.asList("aaa", "bbb", "ccc"));
By the way, you didn't have to use return list.toArray(items.clone()); You could have used, for example, return list.toArray(Arrays.copyOf(items, 0));, where you are passing to list.toArray() an empty array that contains none of the arguments from items.
The whole point of passing an argument to the version of list.toArray() that takes an argument, is to provide an array object whose actual runtime class is the actual runtime class of the array object it wants to return. This could have been achieved with items.clone(), or with items itself (though that would cause list.toArray() to write the resulting elements into the original array pointed to by items which you may not want to happen), or with, as I showed above, an empty array that has the same runtime class.
By the way, the need to pass the argument to list.toArray() is not a generics type issue at all. Even if you had written this with pre-generics Java, you would have had to do the same thing. This is because the version of List::toArray() that took no arguments always returns an array object whose actual runtime class is Object[], as the List doesn't know at runtime what its component type is. To have it return an array object whose actual runtime class is something different, you had to give it an example array object of the right runtime class to follow. That's why pre-generics Java also had the version of List::toArray() that took one argument; even though in pre-generics, both methods were declared to return Object[], they are different as the actual runtime class returned is different.
It's possible add an int and an String in the array ? I said in the same array.
No, Java is a strongly-typed language. So you cannot add a String and an int to the same array if the array is typed as either String or int.
However if your array is typed as Object, you can add a String and an Integer (an integer literal will be autoboxed) to that same array. This is not recommended and is probably a sign that you should think more about your design. The first question you need to ask yourself is why you need to do this. If you do have a valid reason, then it would be better to convert from one to the other instead of having an array typed as Object.
Having a catch-call array where you can shove in any object in a bad idea for many reasons:
You are enforcing no separation between the objects. Are the objects actually related to each other? If so you type then using an interface or create an abstract class that each of the types extend.
Since you have no separation between the objects, anything you pull out of the array is an Object. How would you know what it is? You need to inspect its type explicitly. This is an extremely cumbersome and unmaintainable design.
You essentially end up losing type-safety and will not be able to benefit from type-mismatch errors that will show up during compilation. This will hide possible errors in your code where you may have forgotten to inspect the type, or where you are casting an object to the wrong type. This can lead to all kinds of nightmarish bugs.
Your code is going to be littered with explicit checks and casts and will be unmaintainable (by you or anyone else).
Your code leaks abstraction everywhere. No one can look at the array and realize what the array contains. Anyone who uses your code needs to remember an overwhelming amount of detail as to what types of objects the array can contain.
Obfuscation is never a valid reason. Code should be clear, easy to read, easy to maintain, and easy to understand (for you and for anyone else who will read your code). Any code that looks obfuscated or is "clever" either needs to be rewritten or documented extensively to explain the reason for its "cleverness". As far as obfuscating the source, it is a non-issue since you're going to be distributing the .class files anyway. You can run that through a decompiler to look at the source code. There is nothing you can do at the source level to satisfactorily obfuscate your code; you're only going to make it difficult for you or anyone else to maintain. Obfuscation can be done at the byte-code level and so that doesn't really apply to this situation.
Yes it is possible, but it is not good practice.
Object[] myObjects = new Object[] {array1[i], array2[i], "name1", value1, value2, "name2", value1, value....};
It must be array of objects
Strictly speaking: No.
Otherwise: Yes for most practical purposes:
Object[] array = { 42, "foo" };
Please note, that the 42 is not an int but an `Integer´. But due to autoboxing and unboxing you wont notice the difference. The tradeoff is of course performance and garbage collector overhead.
Also the array must be of type Object[], not of type String[] nor of type int[].
In your string array you could have "123" and then convert it to an int later when you need it.
You can't add a primitive types (including int) to an array with Objects such as String. However, autoboxing of int to Integer will make this possible if you declare an Object[] array.
Object[] array = new Object[2];
array[0] = "Hello";
array[1] = 42;
Though I wouldn't recommend doing this if modeling this String and int as attributes of a class would work.
You can use java.util.ArrayList to do this. You will need to make sure that you check carefully what you are getting when you pull items out though.
Yes it definitely is possible, just have an array of raw objects.
For example:
Object[] arr = new Object[10];
arr[0] = 10; // boxed to Integer class
arr[1] = "foo"; // String class
Then you can use instanceof to determine the type of object stored at a particular index.
For example:
if (arr[0] instanceof Integer) ((Integer) arr[0]) += 10;
Note that this is not necessarily a good practise to get used to, but it does have applications.
I was just looking at the method defined in the List interface:
Returns an array containing all of the elements in this list in the correct order; the runtime type of the returned array is that of the specified array. If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.
If the list fits in the specified array with room to spare (i.e., the array has more elements than the list), the element in the array immediately following the end of the collection is set to null. This is useful in determining the length of the list only if the caller knows that the list does not contain any null elements.
<T> T[] toArray(T[] a);
And I was just wondering why is it implemented this way, basically if you pass it an array with a length < to the list.size(), it will simply create a new one and return it. Therefore the creation of the new Array Object in the method parameter is useless.
Additionally if you pass it an array long enough using the size of the list if returns that same object with the objects - really no point in returning it since it is the same object but ok for clarity.
The problem is that I think this promotes slightly inefficient code, in my opinion toArray should simply receive the class and just return the new array with the contents.
Is there any reason why it is not coded that way?.
357 public <T> T[] toArray(T[] a) {
358 if (a.length < size)
359 // Make a new array of a's runtime type, but my contents:
360 return (T[]) Arrays.copyOf(elementData, size, a.getClass());
361 System.arraycopy(elementData, 0, a, 0, size);
362 if (a.length > size)
363 a[size] = null;
364 return a;
365 }
Maybe so it has a runtime type?
From wiki:
Consequently, instantiating a Java
class of a parameterized type is
impossible because instantiation
requires a call to a constructor,
which is unavailable if the type is
unknown.
As mentioned by others, there are a couple different reasons:
You need to pass in the type somehow, and passing in an array of the specified type isn't an unreasonable way to do it. Admittedly, it might be nice if there was a version that you pass in the Class of the type you want too, for speed.
If you want to reuse your array, you can keep passing in the same one, rather than needing to create a new one each time. This can save time and memory, and GC issues if you need to do it many, many times
Most likely this is to allow you to reuse arrays, so you basically avoid (relatively costly) array allocation for some use cases. Another much smaller benefit is that caller can instantiate array slightly more efficiently, since toArray() must use 'java.lang.reflect.Array.newInstance' method.
This method is a holdover from pre-1.5 Java. Here is the link to javadoc
Back then it was the only way to convert a list to a reifiable array.
It is an obscure fact, but although you can store anything in the Object[] array, you cannot cast this array to more specific type, e.g.
Object[] generic_array = { "string" };
String[] strings_array = generic_array; // ClassCastException
Seemingly more efficient List.toArray() does just that, it creates a generic Object array.
Before Java generics, the only way to do a type-safe transfer was to have this cludge:
String[] stronglyTypedArrayFromList ( List strings )
{
return (String[]) strings.toArray( new String[] );
// or another variant
// return (String[]) strings.toArray( new String[ strings.size( ) ] );
}
Thankfully generics made these kind of machinations obsolete. This method was left there to provide backward compatibility with pre 1.5 code.
My guess is that if you already know the concrete type of T at the point you're calling toArray(T[]), it's more performant to just declare an array of whatever it is than make the List implementation call Arrays.newInstance() for you -- plus in many cases you can re-use the array.
But if it annoys you, it's easy enough to write a utility method:
public static <E> E[] ToArray(Collection<E> c, Class<E> componentType) {
E[] array = (E[]) Array.newInstance(componentType, c.size());
return c.toArray(array);
}
(Note that there's no way to write <E> E[] ToArray(Collection<E> c), because there's no way to create an array of E at runtime without a Class object, and no way to get a Class object for E at runtime, because the generics have been erased.)