Java code:
Transformer TRANSFORM_TO_INTEGER = new Transformer() {
public Object transform(Object input) {
Integer i = new Integer((String) input);
return i;
}
};
String begin = "1,2,3,4,5";
List strList = Arrays.asList(StringUtils.split(begin, ","));
CollectionUtils.transform(strList, TRANSFORM_TO_INTEGER);
This code would throw ArrayStoreException:
java.lang.ArrayStoreException
at java.util.Arrays$ArrayList.set(Arrays.java:2360)
at java.util.AbstractList$ListItr.set(AbstractList.java:488)
at org.apache.commons.collections.CollectionUtils.transform(CollectionUtils.java:434)
Why is that?
The ArrayStoreException occurs when an attempt is made to store an object of an incorrect type is placed into an array.
What is the code doing?
In the example code given, the CollectionUtil.transform method takes a Collection and performs an in-place transform of the elements, which means that Objects are taken out of the original Collection (such as a List) and placed back into the same Collection.
The code for the Transformer takes a String and transforms it into a Integer -- this is core issue here -- the type of object is changing when the transform is applied.
What could be going wrong?
As previously mentioned, CollectionUtil.transform will use the given Transformer and perform the transformation on each element in the Collection and store it back to the original Collection, which is the strList.
I suspected that the List created by Arrays.asList is being backed by a String[], as that would be the likely be the source of the ArrayStoreException. Running the debugger confirmed that, as it was backed by a String[5]. (Using Eclipse, running on JRE 6 on Windows.)
What does the this example illustrate?
This is a prime example of how the lack of generics allows code that is not typesafe to be written, and consequently, a problem arises at runtime. If the code had been written with generics (and Apache Commons Collection supported it) these types of problems would be caught at compile time.
The bottom line -- one cannot transform type elements in a List -- if the List contains Strings, the Transformer.transform should only return a String.
What can be done?
As an alternative, Google Collections has a Collections2.transform method, which takes a given Collection and returns a Collection transformed by a Function.
This method supports generics, so it is typesafe, and the fact it returns a new Collection means that the types can change through the transformation.
The Arrays.asList method uses the same supplied array as the backing array for the new list instance. The API code looks like the following:
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
The call to StringUtils.split creates a String[] which is passed to the Arrays.asList method. This would restrict the type of elements that you can insert into the new list instance to only String objects.
CollectionUtils class supports 2 different types of transformations:
In place transformation - In this case the input collection instance gets updated with the transformed values. All the transform() variants fall in this category. When using Collection types which are backed by arrays (e.g. ArrayList) the transformation can be successful only when the transformed values are type compatible with the backing array type. This explains the exception that you are seeing.
Out of place transformation - In this case the input collection is never updated. Instead the transformed values are collected in a separate collection instance. All the collect() variants fall in this second category. The overloaded versions of collect() method either accepts the output collection as an argument or if no separate collection is specified creates a new list instance to collect the transformed values.
Based on the scenario that you are trying to address you should go with the 2nd type of transformation and call one of the collect() variants.
Related
I recently started working in Java streams. I was trying to get the String values out of the result set of one of my SQL queries. The result set selects just a String/Varchar column from the DB.
So I did:
List<String> list = query.getResultList().stream().map(Object::toString).collect(Collectors.toList());
or:
List<String> list = = query.getResultList().stream().map(String::valueOf).collect(Collectors.toList());
I believe the map takes a function to convert data from one type to another. In this case, from Object to String and then collect them in a String list.
But the above code shows compile time error: Cannot convert from Object to List of string.
Please suggest me the correct way of doing this and explain what is wrong with my understanding.
Because Query.getResultList() returns a raw type List it will break stream pipeline which is heavily based on generic type information. Raw type are effectively removing all information about generic types when used so stream collector returns Object.
You can work around it by manually introducing the generic type with a cast to List<?>:
List<String> collect = ((List<?>) query.getResultList()).stream()
.map(Object::toString)
.collect(Collectors.toList());
Use a TypedQuery<String> instead of a Query.
This does away with the rather superfluous remapping, and introduces type-safeness.
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.
I have an object that's designed to paginate a list and send a page of the results to a user. However, I want to be able to use Functions to pass behavior to this object. The behavior will be used by the object to dynamically generate the content of the lines using methods inside the objects in the list.
public class InteractiveJSONList<T> {
private final List<T> list;
private List<Function<T, Object>> formatArgFunctionSequence;
// ... Irrelevant code truncated.
// Constructor
public InteractiveJSONList(List<T> list) {
this.list = list;
}
}
I'm using generics to define the type of list, and likewise, the input type of the function. However, when attempting to define the Function as input to a method, NetBeans doesn't seem to recognize the input object as the correct type (it simply treats it as an instance of Object).
This code snippet resides inside an instance method in InteractiveJSONList. The method is designed to extract information from each item in the list and use the results as format arguments for String.format later on.
for (T item : itemsList) {
index++;
// Create a new JSON Message.
new JSONMessage(resultLinesBaseMessage)
.setFormatArguments(index, stringFunctionSequence.stream()
.map(o -> o.apply(item))
.collect(Collectors.toList())
.toArray(new Object[stringFunctionSequence.size()]))
}
I'm using Java 8's Stream methods to apply the function using each item in the list as input, then collecting the results and submitting them as an array for a varArgs method to use later in String.format.
Finally, this is an attempt to implement the mechanism.
List<String> data = ... //Using Strings at this point, but could be anything.
new InteractiveJSONList<String>(data)
.setStringBuildingSequence(input -> input.substring(0,5));
I'm only using String and its method substring as an example for how I want this mechanism to work. Certain objects in this list will be more complicated than Strings, and will have getters for variables inside the object. I want to define the behavior required to get variables from within the items in this list, and then use them to generate dynamic results. However, NetBeans won't recognize input as a member of String, and I subsequently am not able to use String's methods. input is only recognized as an object, even though InteractiveJSONList's generic type T is defined as a String when the object was created.
Am I setting up generics incorrectly? I might be led to think it's as simple as that, however, I tested this instead:
List<String> data = ... //Using Strings at this point, but could be anything.
Function<String, Object> testFunction = input -> new InteractiveText(input.substring(0, 8));
new InteractiveJSONList<String>(data)
.setStringBuildingSequence(testFunction);
And it compiled and ran correctly.
Any advice? Also, if this is a terrible practice for my desired purpose, please let me know. Thank you!
I am in a data structures class and have had about a year and half of experience with JAVA. I am trying to understand code which is not clearly defined in the book. In the following line:
Queue<Integer> encodingQueue = new LinkedList<Integer>();
I understand that we are creating a new LinkedList object of Queue type. My questions are:
what is the <> used with Integer for? And why is it also with the LinkedList object and not in the parameters?
Why is the word Integer used and not int?
Thanks!
good luck in your data structures class!
Generics
In Java, these data structures (i.e. Queue, LinkedList, ...) can hold any kind of object. More often than not, though, a given instance of a Queue or LinkedList will hold a collection of the same type (here integers).
The angle-bracket syntax is used to specify which types are allowed in this specific instance of the collection.
So, the way to read:
Queue<Integer> encodingQueue = new LinkedList<Integer>();
is...
"Create a new linked-list that holds only integers, and assign that instance to the reference variable 'encodingQueue' which will be treated like a queue that holds only integers."
This feature that you're using is called "Generics". You can read up more about it here: http://en.wikipedia.org/wiki/Generics_in_Java
Autoboxing
In Java, there are two kinds of types: primitives and object references. "int" is a primitive and "Integer" is a class.
You cannot create a reference to a primitive in Java and collections (like LinkedList) only hold object references. So, you cannot stuff a primitive into a collection (e.g. you cannot put "int"s into a LinkedList). This is why Java provides the object equivalent for primitives: so that you can create collections of things like integers, floats, booleans, etc.
It can be a little confusing when you first start using primitives; Java will attempt to automatically convert primitives to object references when that's what's clearly needed (and vice-versa). This feature is called "autoboxing".
List myList = new LinkedList<Integer>();
myList.add(2);
Here, 2 is a primitive. But the compiler knows that you need an object reference, not a primitive. So, it automatically "boxes-up" this value by (in the background) creating a new instance of the class Integer and setting it's value to 2.
So, that's equivalent to (and this is what actually happens):
List myList = new LinkedList<Integer>();
myList.add(Integer.valueOf(2));
Where Integer.valueOf() first looks in an internal cache to see if an instance already exists for that value. If it does, that's returned, otherwise a new Integer object for that value is created. (Thank you, Boris, for pointing this out)
http://docs.oracle.com/javase/tutorial/java/generics/
Used to specify type parameters. It's how you pass a type into a class. Lists/queues have them, because it's the type of Object that the list holds.
Integer is used instead of int because ints are not Objects. Integer is just the wrapper class for it.
Integer is a object based wrapper version of the primitive 'int'. This allows the basic type to play well in object oriented language. Read more here:
They can be used interchangably native and wrapper, this is called 'autoboxing/unboxing'
http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html
The <> are part of Java Generics (A way to tell the compiler what object you expect to work with), more here:
http://docs.oracle.com/javase/tutorial/java/generics/
Happy learning!
That infers the type of object to be stored in the Queue. You'll want to look into Java Generics.
Collections store objects, int is a primitive type, Integer is the Java object representation. The compiler will autobox any ints you attempt to put in the Queue as Integers.
1) <Integer> is used to specify at compile-time that the objects contained in your Collection are going to be Integers, so for instance this will not compile:
Double d = 10d;
encodingQueue.add(d); <-- Compilation error
2) The reason why Integer is used instead of int is that you cannot store into collections primitive data types but you have to use objects; you can still write encodingQueue.add(10) thanks to the automatic boxing of ints inside Integers, but when you declare a typed collection (or, in general, a parameterized class) you have to use a class that extends Object as the parameter.
the <Integer> specifies the type of the data that the object is going to hold. it is Integer instead of int because the LinkedList is designed to hold an Object and not a primitive. so
Queue<Integer> q = new LinkedList<Integer>();
means that we are creating an Object of LinkedList that holds Integer type objects and we are refering it with a Queue reference variable.
what is the <> used with Integer for? And why is it also with the
LinkedList object and not in the parameters?
If you have a Parameterized Collection Queue<Integer> than a Raw type Queue
it ensures that none of the Objects other than Integer are added in the Collection.
This feature was introduced from Java 1.5
Why is the word Integer used and not int?.
Collection deals with Objects and not primitives.
Consider this example from Effective Java Item 23
// Now a raw collection type - don't do this!
/**
* My stamp collection. Contains only Stamp instances.
*/
private final Collection stamps = ... ;
If you accidentally put a coin into your stamp collection, the
erroneous insertion compiles and runs without error:
// Erroneous insertion of coin into stamp collection
stamps.add(new Coin( ... ));
You don’t get an error until you retrieve the coin from the stamp
collection:
// Now a raw iterator type - don't do this!
for (Iterator i = stamps.iterator(); i.hasNext(); ) {
Stamp s = (Stamp) i.next(); // Throws ClassCastException
... // Do something with the stamp
}
Example with Parameterized
// Parameterized collection type - typesafe
private final Collection<Stamp> stamps = ... ;
From this declaration the compiler knows that stamps should contain
only Stamp instances and guarantees this to be the case, assuming your
entire code base is compiled with a compiler from release 1.5 or
later and all the code compiles with- out emitting (or suppressing;
see Item 24) any warnings. When stamps is declared with a
parameterized type, the erroneous insertion generates a compile-time
error message that tells you exactly what is wrong:
Test.java:9: add(Stamp) in Collection<Stamp> cannot be applied to (Coin)
stamps.add(new Coin());
They are a type of generic (see #JTigger's answer) this means they can be a queue/list of ANY Java object including custom Java objects, this allows you to use the queue/list functions on anything inside of Java without reinventing the wheel.
Example:
Say we have the custom Object "planet":
public Class Planet {
private String name;
public Planet(String name) { this.name = name; }
public String getName() { return name; }
}
and we want to put a list of planets inside a queue. We could create said queue, and use the already built add() and remove() methods. If Queue was not a Generic class we would have to build our own add() and remove() functions. So using Generics we can list all the names of the planets in the solar system:
public static void main (String args[]) {
Queue<Planet> solarSystem = new LinkedList<Planet>();
solarSystem.add(new Planet("Mercury"));
solarSystem.add(new Planet("Venus"));
solarSystem.add(new Planet("Earth"));
solarSystem.add(new Planet("Mars"));
solarSystem.add(new Planet("Jupiter"));
solarSystem.add(new Planet("Saturn"));
solarSystem.add(new Planet("Uranus"));
solarSystem.add(new Planet("Neptune"));
solarSystem.add(new Planet("Pluto")); //For Nostalgia's sake
for (int i = 0; i < 9; i++) {
System.out.println(solarSystem.element().getName());
solarSystem.remove();
}
}
I know that the Collection framework allows for the creation of "views", that is lightweight "wrappers" for a Collection object.
What I am especially interested in is, given a List, to return a view for only a subset of elements matching some conditions.
Basically, what I want to emulate is the functionality of the subList() method, only not based on start and end indexes, but on some parameters of the elements.
The first approach I thought about was simply to create another List, go through the first List and check each element...
While this wouldn't be actually copy any MyObject but only their references, I would anyways create a new List object, with its overhead. Isn't that right?
Is there any lightweight method of doing what I need?
N.B. My original List is a really big collection...
Thank you all
You can do this easily in Java using the Guava collections (Collections2 has a filter method http://docs.guava-libraries.googlecode.com/git-history/v11.0.1/javadoc/index.html).
You can also do this in groovy using the findAll method, for example
myList.findAll { it.contains("aValue") }
Any of these methods will create a new collection under the hood. So they are just doing the work for you of iterating over the elements and checking them. The overhead of creating a new list is minimal (it's just instantiating one new object).
I would anyways create a new List object, with its overhead
I don't understand what your concern here. Looking at source of ArrayList class even subList(int fromIndex, int toIndex) method in List class creates a new inner class (which extends from List). That is essentially what you will be doing in your method i.e. create a new List instance and copy your matching element's reference into it. That custom method will be more or less will have same performance as subList method.