My goal here is to implement a method that will concatenate an arbitrary number of arrays into a single array of their common supertype, returning the resulting (typed) array. I have two implementations.
The first (this one doesn't need to be simplified):
public static <T> T[] concatArrays(Class<T> type, T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arr.length;
}
T[] all = (T[]) Array.newInstance(type, totalLen);
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
Let's create some arrays:
Long[] l = { 1L, 2L, 3L };
Integer[] i = { 4, 5, 6 };
Double[] d = { 7., 8., 9. };
Our method is called with:
Number[] n = concatArrays(Number.class, l, i, d);
This works and is completely type-safe (e.g., concatArrays(Long.class, l, i, d) is a compiler error), but it's somewhat annoying to specify Number.class if it's not necessary. So I implemented the following method (this is the one I want to simplify):
public static <T> T[] arrayConcat(T[] arr0, T[]... rest) {
Class commonSuperclass = arr0.getClass().getComponentType();
int totalLen = arr0.length;
for (T[] arr: rest) {
totalLen += arr.length;
Class compClass = arr.getClass().getComponentType();
while (! commonSuperclass.isAssignableFrom(compClass)) {
if (compClass.isAssignableFrom(commonSuperclass)) {
commonSuperclass = compClass;
break;
}
commonSuperclass = commonSuperclass.getSuperclass();
compClass = compClass.getSuperclass();
}
}
T[] all = (T[]) Array.newInstance(commonSuperclass, totalLen);
int copied = arr0.length;
System.arraycopy(arr0, 0, all, 0, copied);
for (T[] arr: rest) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
This is nicer to use from the client's perspective:
Number[] n = arrayConcat(l, i, d);
And again, the compiler is smart enough to give an appropriate error on Long[] all = arrayConcat(l, i, d). Since the compiler is able to recognize this error, it is clear that I am performing work at runtime (determining the common superclass of the given arrays) that the compiler is able to perform at compile time. Is there any way to implement my method without using my reflection-based method for determining a common superclass for the array creation step?
I tried this approach:
public static <T> T[] arrayConcat(T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arrays.length;
}
Object[] all = new Object[totalLen];
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return (T[]) all;
}
but this throws a ClassCastException upon returning. Obviously new T[totalLen] is also out. Does anyone have any other ideas?
You can do something like this:
public static <T> T[] arrayConcat(T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arr.length;
}
T[] all = (T[])Array.newInstance(
arrays.getClass().getComponentType().getComponentType(), totalLen);
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
This takes advantage of the fact that when using varargs, the compiler constructs an array of the components, and the array's type is properly set up such that the component type is the vararg elements type. In this case the array has type T[][], so we can extract T and use it to construct our T[].
(One exception is if the caller calls it using a generic type as the varargs type, then the compiler can't construct the proper array type. However, if the caller does this it will generate a warning in the caller code (the infamous varargs generics warning), and so the caller is warned that bad things will happen, so it's not our fault.)
One amazing thing about this solution is that it does not produce the wrong answer even when the user passes zero arrays! (As long as the compiler compiles it successfully, it would have inferred (or been specified explicitly) some concrete type T such that T[] is a valid return type. That type T is given to us in the type of arrays) Apparently the compiler doesn't ever infer correctly in this case.
Note that a caller can manually pass the "arrays" argument, and in such case, it could have runtime type of U[][], where U is a subtype of T. In such a case, our method will return an array of runtime type U[], which is still a correct result, as U[] is a subtype of T[].
Simple answer: no. Although the type checker has done some work as far as inferring T, this information is erased by the time it gets to bytecode. Erasure is at the core of Java generics; understand it, and you'll understand generics.
Btw, generics and arrays don't mix well. If you try to concatenate a bunch of List<String>[]s into a single List<String>[], you're going to get compiler warnings (and general lack of type safety).
Your example #2 is actually doing more work at runtime than the compiler is able to do. Consider:
Number[] longs1 = new Long[] { 1L, 2L, 3L };
Number[] longs2 = new Long[] { 4L, 5L, 6L };
Number[] concatted = arrayConcat(longs1, longs2);
The compiler only knows that concatted is a Number[], but your method will (at runtime) figure out that the common type is actually Long[].
Related
This question already has answers here:
How to convert an ArrayList containing Integers to primitive int array?
(19 answers)
Closed 4 years ago.
How can I convert a List<Integer> to int[] in Java?
I'm confused because List.toArray() actually returns an Object[], which can be cast to neither Integer[] nor int[].
Right now I'm using a loop to do so:
int[] toIntArray(List<Integer> list) {
int[] ret = new int[list.size()];
for(int i = 0; i < ret.length; i++)
ret[i] = list.get(i);
return ret;
}
Is there's a better way to do this?
This is similar to the question
How can I convert int[] to Integer[] in Java?.
With streams added in Java 8 we can write code like:
int[] example1 = list.stream().mapToInt(i->i).toArray();
// OR
int[] example2 = list.stream().mapToInt(Integer::intValue).toArray();
Thought process:
The simple Stream#toArray returns an Object[] array, so it is not what we want. Also, Stream#toArray(IntFunction<A[]> generator) doesn't do what we want, because the generic type A can't represent the primitive type int
So it would be nice to have some stream which could handle the primitive type int instead of the wrapper Integer, because its toArray method will most likely also return an int[] array (returning something else like Object[] or even boxed Integer[] would be unnatural here). And fortunately Java 8 has such a stream which is IntStream
So now the only thing we need to figure out is how to convert our Stream<Integer> (which will be returned from list.stream()) to that shiny IntStream.
Quick searching in documentation of Stream while looking for methods which return IntStream points us to our solution which is mapToInt(ToIntFunction<? super T> mapper) method. All we need to do is provide a mapping from Integer to int.
Since ToIntFunction is functional interface we can provide its instance via lambda or method reference.
Anyway to convert Integer to int we can use Integer#intValue so inside mapToInt we can write:
mapToInt( (Integer i) -> i.intValue() )
(or some may prefer: mapToInt(Integer::intValue).)
But similar code can be generated using unboxing, since the compiler knows that the result of this lambda must be of type int (the lambda used in mapToInt is an implementation of the ToIntFunction interface which expects as body a method of type: int applyAsInt(T value) which is expected to return an int).
So we can simply write:
mapToInt((Integer i)->i)
Also, since the Integer type in (Integer i) can be inferred by the compiler because List<Integer>#stream() returns a Stream<Integer>, we can also skip it which leaves us with
mapToInt(i -> i)
Unfortunately, I don't believe there really is a better way of doing this due to the nature of Java's handling of primitive types, boxing, arrays and generics. In particular:
List<T>.toArray won't work because there's no conversion from Integer to int
You can't use int as a type argument for generics, so it would have to be an int-specific method (or one which used reflection to do nasty trickery).
I believe there are libraries which have autogenerated versions of this kind of method for all the primitive types (i.e. there's a template which is copied for each type). It's ugly, but that's the way it is I'm afraid :(
Even though the Arrays class came out before generics arrived in Java, it would still have to include all the horrible overloads if it were introduced today (assuming you want to use primitive arrays).
In addition to Commons Lang, you can do this with Guava's method Ints.toArray(Collection<Integer> collection):
List<Integer> list = ...
int[] ints = Ints.toArray(list);
This saves you having to do the intermediate array conversion that the Commons Lang equivalent requires yourself.
The easiest way to do this is to make use of Apache Commons Lang. It has a handy ArrayUtils class that can do what you want. Use the toPrimitive method with the overload for an array of Integers.
List<Integer> myList;
... assign and fill the list
int[] intArray = ArrayUtils.toPrimitive(myList.toArray(new Integer[myList.size()]));
This way you don't reinvent the wheel. Commons Lang has a great many useful things that Java left out. Above, I chose to create an Integer list of the right size. You can also use a 0-length static Integer array and let Java allocate an array of the right size:
static final Integer[] NO_INTS = new Integer[0];
....
int[] intArray2 = ArrayUtils.toPrimitive(myList.toArray(NO_INTS));
Java 8 has given us an easy way to do this via streams...
Using the collections stream() function and then mapping to ints, you'll get an IntStream. With the IntStream we can call toArray() which gives us int []
int [] ints = list.stream().mapToInt(Integer::intValue).toArray();
to int []
to IntStream
Use:
int[] toIntArray(List<Integer> list) {
int[] ret = new int[list.size()];
int i = 0;
for (Integer e : list)
ret[i++] = e;
return ret;
}
This slight change to your code is to avoid expensive list indexing (since a List is not necessarily an ArrayList, but it could be a linked list, for which random access is expensive).
Here is a Java 8 single line code for this:
public int[] toIntArray(List<Integer> intList){
return intList.stream().mapToInt(Integer::intValue).toArray();
}
If you are simply mapping an Integer to an int then you should consider using parallelism, since your mapping logic does not rely on any variables outside its scope.
int[] arr = list.parallelStream().mapToInt(Integer::intValue).toArray();
Just be aware of this
Note that parallelism is not automatically faster than performing operations serially, although it can be if you have enough data and processor cores. While aggregate operations enable you to more easily implement parallelism, it is still your responsibility to determine if your application is suitable for parallelism.
There are two ways to map Integers to their primitive form:
Via a ToIntFunction.
mapToInt(Integer::intValue)
Via explicit unboxing with lambda expression.
mapToInt(i -> i.intValue())
Via implicit (auto-) unboxing with lambda expression.
mapToInt(i -> i)
Given a list with a null value
List<Integer> list = Arrays.asList(1, 2, null, 4, 5);
Here are three options to handle null:
Filter out the null values before mapping.
int[] arr = list.parallelStream().filter(Objects::nonNull).mapToInt(Integer::intValue).toArray();
Map the null values to a default value.
int[] arr = list.parallelStream().map(i -> i == null ? -1 : i).mapToInt(Integer::intValue).toArray();
Handle null inside the lambda expression.
int[] arr = list.parallelStream().mapToInt(i -> i == null ? -1 : i.intValue()).toArray();
This simple loop is always correct! no bugs
int[] integers = new int[myList.size()];
for (int i = 0; i < integers.length; i++) {
integers[i] = myList.get(i);
}
I've noticed several uses of for loops, but you don't even need anything inside the loop. I mention this only because the original question was trying to find less verbose code.
int[] toArray(List<Integer> list) {
int[] ret = new int[ list.size() ];
int i = 0;
for( Iterator<Integer> it = list.iterator();
it.hasNext();
ret[i++] = it.next() );
return ret;
}
If Java allowed multiple declarations in a for loop the way C++ does, we could go a step further and do for(int i = 0, Iterator it...
In the end though (this part is just my opinion), if you are going to have a helping function or method to do something for you, just set it up and forget about it. It can be a one-liner or ten; if you'll never look at it again you won't know the difference.
There is really no way of "one-lining" what you are trying to do, because toArray returns an Object[] and you cannot cast from Object[] to int[] or Integer[] to int[].
int[] ret = new int[list.size()];
Iterator<Integer> iter = list.iterator();
for (int i=0; iter.hasNext(); i++) {
ret[i] = iter.next();
}
return ret;
Also try Dollar (check this revision):
import static com.humaorie.dollar.Dollar.*
...
List<Integer> source = ...;
int[] ints = $(source).convert().toIntArray();
With Eclipse Collections, you can do the following if you have a list of type java.util.List<Integer>:
List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int[] ints = LazyIterate.adapt(integers).collectInt(i -> i).toArray();
Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, ints);
If you already have an Eclipse Collections type like MutableList, you can do the following:
MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int[] ints = integers.asLazy().collectInt(i -> i).toArray();
Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, ints);
Note: I am a committer for Eclipse Collections
I would recommend you to use the List<?> skeletal implementation from the Java collections API. It appears to be quite helpful in this particular case:
package mypackage;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Test {
// Helper method to convert int arrays into Lists
static List<Integer> intArrayAsList(final int[] a) {
if(a == null)
throw new NullPointerException();
return new AbstractList<Integer>() {
#Override
public Integer get(int i) {
return a[i]; // Autoboxing
}
#Override
public Integer set(int i, Integer val) {
final int old = a[i];
a[i] = val; // Auto-unboxing
return old; // Autoboxing
}
#Override
public int size() {
return a.length;
}
};
}
public static void main(final String[] args) {
int[] a = {1, 2, 3, 4, 5};
Collections.reverse(intArrayAsList(a));
System.out.println(Arrays.toString(a));
}
}
Beware of boxing/unboxing drawbacks.
Using a lambda you could do this (compiles in JDK lambda):
public static void main(String ars[]) {
TransformService transformService = (inputs) -> {
int[] ints = new int[inputs.size()];
int i = 0;
for (Integer element : inputs) {
ints[ i++ ] = element;
}
return ints;
};
List<Integer> inputs = new ArrayList<Integer>(5) { {add(10); add(10);} };
int[] results = transformService.transform(inputs);
}
public interface TransformService {
int[] transform(List<Integer> inputs);
}
This method is used to split array into chunks reference. I want to make this method generic.
Problem is, I can not initialize array like this.
T[][] arrays = new T[chunks][];
complete method
public <T> T[][] splitArray(T[] arrayToSplit, int chunkSize) {
if (chunkSize <= 0) {
return null;
}
int rest = arrayToSplit.length % chunkSize;
int chunks = arrayToSplit.length / chunkSize + (rest > 0 ? 1 : 0);
T[][] arrays = new T[chunks][];
for (int i = 0; i < (rest > 0 ? chunks - 1 : chunks); i++) {
arrays[i] = Arrays.copyOfRange(arrayToSplit, i * chunkSize, i * chunkSize + chunkSize);
}
if (rest > 0) {
arrays[chunks - 1] = Arrays.copyOfRange(arrayToSplit, (chunks - 1) * chunkSize, (chunks - 1) * chunkSize + rest);
}
return arrays;
}
What is correct way to initialize generic array?
You cannot initialise arrays with a generic parameter. That is a restriction on generics.
A workaround is to create an Object[][] and cast it to T[][]:
T[][] arrays = (T[][])new Object[chunks][];
You can't create a generic array, but you can declare a generic array.
T[] test = null; // works
T[] test2 = new T[10]; // fails
T[] test3 = (T[]) new Object[10]; // works
At the same time, you should be careful with this
You can't make a generic array directly since type variable information is lost at runtime due to erasure.
However, in your case there is a workaround, since the output type of the method is just an array of type arrayToSplit and arrays in Java do have their type information available at runtime.
So instead of:
T[][] arrays = new T[chunks][];
you can do:
T[][] arrays = (T[][])Array.newInstance(arrayToSplit.getClass(), chunks);
Mixing arrays and generics can be confusing and error-prone though. If possible, I would use the collections API where possible and use List<T> instead of arrays.
Guava even has a method that I think does exactly what you want:
Lists.partition(List, int)
Returns consecutive sublists of a list, each of the same size (the final list may be smaller). For example, partitioning a list containing [a, b, c, d, e] with a partition size of 3 yields [[a, b, c], [d, e]] -- an outer list containing two inner lists of three and two elements, all in the original order.
I've been coding with C++ in school for 3 years now. I've started coding in Java just 2 days ago; my question is:
Is it bad practice to make generic arrays? What would be the alternative?
I am stumped and I can't seem to make a generic array besides doing something weird such as this example:
//Class implementing the MergeSort algorithm with generic types
// Revised by Doina January 2014
package Sorting;
import java.lang.*;
public class MergeSort {
// Wrapper method for the real algorithm
// T is the generic type which will be instantiated at runtime
// elementas are required to be comparable
public static <T extends Comparable<T>> void sort(T[] a) {
mergesort(a, 0, a.length - 1);
}
// Recursive mergesort method, following the pseudocode
private static <T extends Comparable<T>> void mergesort(T[] a, int i, int j) {
if (j - i < 1) return;
int mid = (i + j) / 2;
mergesort(a, i, mid);
mergesort(a, mid + 1, j);
merge(a, i, mid, j);
}
// Merge method
// Here we need to allocate a new array, but Java does not allow allocating arrays of a generic type
// As a work-around we allocate an array of type Object[] the use type casting
// This would usually generate a warning, which is suppressed
#SuppressWarnings("unchecked")
private static <T extends Comparable<T>> void merge(T[] a, int p, int mid, int q) {
Object[] tmp = new Object[q - p + 1];
int i = p;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= q) {
if (a[i].compareTo(a[j]) <= 0)
tmp[k] = a[i++];
else
tmp[k] = a[j++];
k++;
}
if (i <= mid && j > q) {
while (i <= mid)
tmp[k++] = a[i++];
} else {
while (j <= q)
tmp[k++] = a[j++];
}
for (k = 0; k < tmp.length; k++) {
a[k + p] = (T) (tmp[k]); // this is the line that woudl generate the warning
}
}
// Main methos to test the code, using Integer Objects
public static void main(String[] args) {
Integer[] a = new Integer[5];
a[0] = new Integer(2);
a[1] = new Integer(1);
a[2] = new Integer(4);
a[3] = new Integer(3);
a[4] = new Integer(-1);
// T will be instantiated to Integer as a resutl of this call
MergeSort.sort(a);
// Print the result after the sorting
for (int i = 0; i < a.length; i++)
System.out.println(a[i].toString());
}
}
It's not that it's a bad idea per se; it's just that generics and arrays don't mix very well.
The reason is due to covariance and invariance. Arrays are covariant (Integer[] is an Object[] because Integer is an Object, but generic classes are invariant (List<Integer> is not a List<Object> even though an Integer is an Object).
You also have to deal with unchecked casts, which defeat the entire purpose of generics. The most common way to create a generic array - E[] foo = (E[]) new Object[10]; - is not type-safe and can't be enforced at compile time. It's possible to reason about it at runtime, but the compile-time checks which generics bring to the table are lost at that point.
To answer the question directly, where and when possible, you want to use Java Collections instead, as they play very nicely with generics.
Just glancing at your supplied code, I imagine that using List<T> instead of T[] would get you by most of your problems (and I would hope that you're passing an ArrayList in since those operations can become expensive with a linked list).
It's not bad practice to create a generic array, but doing so correctly is so cumbersome people usually avoid it.
The reason it is cumbersome is that generics are erased, while arrays are reified. That is, type parameters are erased during compilation, while the component type of arrays is retained. Therefore, the runtime knows the component type of every array, but has forgotten the type arguments of all objects, i.e. the line
E[] array = new E[10];
does not compile because the runtime needs to know the component type for the new array, but has forgotten was E is.
The workaround in Makoto's answer:
E[] array = (E[]) new Object[10];
is not a good idea, as it actually creates an Object[], but then pretends to the compiler that is an E[]. As the the runtime has forgotten was E is, this cast also succeeds at runtime, even though it is not type correct. However, the runtime still enforces memory safety, by performing an additional check as soon as it is able, i.e. when the object is stored in a variable whose type is not generic. For instance:
static <E> E[] createArray(int size) {
return (E[]) new Object[size];
}
public static void main(String[] args) {
String[] array = createArray(size); // throws ClassCastException
for (String s : array) {
// whatever
}
}
That is, this workaround is hack that only works under certain circumstances, and will cause highly puzzling behaviour otherwise (a ClassCastException in a line of code that does not contain a cast ...).
The only way to create an E[] is through reflection, by providing the class object of our desired component type:
Class<E> eClass = ...;
E[] array = Arrays.newInstance(eClass, 10);
but how can we get this class object? If our caller knows, they can pass us a class literal (like Integer.class), or we can use reflection on some other object. In your case, you have another E[] at hand, so you can ask that array what E is:
E[] originalArray = ...;
Class<E> eClass = (Class<E>) originalArray.getClass().getComponentType();
E[] newArray = (E[]) Array.newInstance(eClass, size);
This will ensure the new array is of the same type as the old one, which is E[], unless somebody lied to us about the type of that array using Makoto's workaround.
As you can see, it is possible to create a generic array, but it is so cumbersome that people usually go the great lengths to avoid it. The usual alternative are using an array of some super type (in your merge sort, Comparable[] might work even better than Object[], because you would not have to cast), or using an ArrayList instead.
Adding to Makoto's answer,I would say that Arrays are covariant due to the fact that their type information is available at runtime whereas generic classes are invariant as type information is not available due to type erasure at compile time.
Covariant Arrays :-
Object[] covariantArrays = new String[5];
covariantArrays[0] = new Dog(); // will give java.lang.ArrayStoreException
Invariant Arrays :-
List invariantArrays = new List<String>();
invariantArrays.add(new Dog()); // Works fine as type information is not available
For this reason Generic arrays don't go well as Generics are limited to compile type safety and Arrays have real type information available even at Runtime
I'm brushing up on my data structure skills. I found a great free book online called Open Data Structures in Java. After reading through it, I'm trying to create all the stated data structures with the code provided so I can instill them in to my memory.
I ran in to an "error" and for the life of me I can't figure it out: in the resize() method for the ArrayStack (section 2.1.2), there is the line of code - T[] b = newArray(Math.max(n*2,1));. The point of this is so the array, which contains the elements, is neither too small or too large. If I use this line of code I get the following error message from Eclipse:
The method newArray(int) is undefined for the type ArrayStack<T>.
So, I'm thinking that it must have been a "typo" and what was meant was "new Array". But fixing that leaves me with the following error message from Eclipse:
Type mismatch: cannot convert from Array to T[].
I don't understand what I'm missing or doing wrong. So to sum up my question, how do you declare and instantiate a new generic array, particularly at a fixed size?
Given the class of T, let's call it klass...
For a one-dimensional array of length n:
T[] arr = (T[]) Array.newInstance(klass, n)
For a two-dimensional array of length n x m:
T[][] 2dArr = (T[][]) Array.newInstance(klass, n, m)
The above are actually two different functions, one takes an int argument and the second takes an int... argument, which you can also pass as an array. Both return an Object for which you need an unchecked cast.
If you want a jagged array of length n, second dimension undetermined, you will have to get the class of T[], let's call it klass2, and then do
T[][] 2dArr2 = (T[][]) Array.newInstance(klass2, n)
This is why you also need to pass in a type to collection.toArray(T[] arr), otherwise you get an Object[] for the vanilla toArray() method because it doesn't know the type.
What you would like is:
void resize() {
T[] b = new T[Math.max(n*2,1)];
for (int i = 0; i < n; i++) {
b[i] = a[i];
}
a = b;
}
But that does not work because T is not actually known at runtime, and it would have to be. However this can be written with a generic-safe constructor.
void resize() {
T[] b = (T[]) Array.newInstance( a.getClass().getComponentType(),
Math.max(n*2,1) );
for (int i = 0; i < n; i++) {
b[i] = a[i];
}
a = b;
}
It appears that the author meant to have a method, newArray in that class:
void T[] newArray(int size) {
return (T[]) Array.newInstance( a.getClass().getComponentType(), size);
}
Java does not make this a simple matter. Because of type erasure the class of T is not available at runtime (which is when you need to determine what type of array to create).
However, since you already have an array (a), you can use reflection to create a new array of that type.
It will look something like this:
import java.lang.reflect.Array;
public class Test {
public static void main(String args[]) throws Exception {
Object array[] = new Object[5];
array = resizeArray(array, 10);
for (Object o : array) {
System.out.println(o);
}
}
public static <T>
T[] resizeArray(T[] a, int newSize) throws Exception {
T[] b = (T[]) Array.newInstance(a.getClass().getComponentType(),
newSize);
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
return b;
}
}
This question already has answers here:
How to convert an ArrayList containing Integers to primitive int array?
(19 answers)
Closed 4 years ago.
How can I convert a List<Integer> to int[] in Java?
I'm confused because List.toArray() actually returns an Object[], which can be cast to neither Integer[] nor int[].
Right now I'm using a loop to do so:
int[] toIntArray(List<Integer> list) {
int[] ret = new int[list.size()];
for(int i = 0; i < ret.length; i++)
ret[i] = list.get(i);
return ret;
}
Is there's a better way to do this?
This is similar to the question
How can I convert int[] to Integer[] in Java?.
With streams added in Java 8 we can write code like:
int[] example1 = list.stream().mapToInt(i->i).toArray();
// OR
int[] example2 = list.stream().mapToInt(Integer::intValue).toArray();
Thought process:
The simple Stream#toArray returns an Object[] array, so it is not what we want. Also, Stream#toArray(IntFunction<A[]> generator) doesn't do what we want, because the generic type A can't represent the primitive type int
So it would be nice to have some stream which could handle the primitive type int instead of the wrapper Integer, because its toArray method will most likely also return an int[] array (returning something else like Object[] or even boxed Integer[] would be unnatural here). And fortunately Java 8 has such a stream which is IntStream
So now the only thing we need to figure out is how to convert our Stream<Integer> (which will be returned from list.stream()) to that shiny IntStream.
Quick searching in documentation of Stream while looking for methods which return IntStream points us to our solution which is mapToInt(ToIntFunction<? super T> mapper) method. All we need to do is provide a mapping from Integer to int.
Since ToIntFunction is functional interface we can provide its instance via lambda or method reference.
Anyway to convert Integer to int we can use Integer#intValue so inside mapToInt we can write:
mapToInt( (Integer i) -> i.intValue() )
(or some may prefer: mapToInt(Integer::intValue).)
But similar code can be generated using unboxing, since the compiler knows that the result of this lambda must be of type int (the lambda used in mapToInt is an implementation of the ToIntFunction interface which expects as body a method of type: int applyAsInt(T value) which is expected to return an int).
So we can simply write:
mapToInt((Integer i)->i)
Also, since the Integer type in (Integer i) can be inferred by the compiler because List<Integer>#stream() returns a Stream<Integer>, we can also skip it which leaves us with
mapToInt(i -> i)
Unfortunately, I don't believe there really is a better way of doing this due to the nature of Java's handling of primitive types, boxing, arrays and generics. In particular:
List<T>.toArray won't work because there's no conversion from Integer to int
You can't use int as a type argument for generics, so it would have to be an int-specific method (or one which used reflection to do nasty trickery).
I believe there are libraries which have autogenerated versions of this kind of method for all the primitive types (i.e. there's a template which is copied for each type). It's ugly, but that's the way it is I'm afraid :(
Even though the Arrays class came out before generics arrived in Java, it would still have to include all the horrible overloads if it were introduced today (assuming you want to use primitive arrays).
In addition to Commons Lang, you can do this with Guava's method Ints.toArray(Collection<Integer> collection):
List<Integer> list = ...
int[] ints = Ints.toArray(list);
This saves you having to do the intermediate array conversion that the Commons Lang equivalent requires yourself.
The easiest way to do this is to make use of Apache Commons Lang. It has a handy ArrayUtils class that can do what you want. Use the toPrimitive method with the overload for an array of Integers.
List<Integer> myList;
... assign and fill the list
int[] intArray = ArrayUtils.toPrimitive(myList.toArray(new Integer[myList.size()]));
This way you don't reinvent the wheel. Commons Lang has a great many useful things that Java left out. Above, I chose to create an Integer list of the right size. You can also use a 0-length static Integer array and let Java allocate an array of the right size:
static final Integer[] NO_INTS = new Integer[0];
....
int[] intArray2 = ArrayUtils.toPrimitive(myList.toArray(NO_INTS));
Java 8 has given us an easy way to do this via streams...
Using the collections stream() function and then mapping to ints, you'll get an IntStream. With the IntStream we can call toArray() which gives us int []
int [] ints = list.stream().mapToInt(Integer::intValue).toArray();
to int []
to IntStream
Use:
int[] toIntArray(List<Integer> list) {
int[] ret = new int[list.size()];
int i = 0;
for (Integer e : list)
ret[i++] = e;
return ret;
}
This slight change to your code is to avoid expensive list indexing (since a List is not necessarily an ArrayList, but it could be a linked list, for which random access is expensive).
Here is a Java 8 single line code for this:
public int[] toIntArray(List<Integer> intList){
return intList.stream().mapToInt(Integer::intValue).toArray();
}
If you are simply mapping an Integer to an int then you should consider using parallelism, since your mapping logic does not rely on any variables outside its scope.
int[] arr = list.parallelStream().mapToInt(Integer::intValue).toArray();
Just be aware of this
Note that parallelism is not automatically faster than performing operations serially, although it can be if you have enough data and processor cores. While aggregate operations enable you to more easily implement parallelism, it is still your responsibility to determine if your application is suitable for parallelism.
There are two ways to map Integers to their primitive form:
Via a ToIntFunction.
mapToInt(Integer::intValue)
Via explicit unboxing with lambda expression.
mapToInt(i -> i.intValue())
Via implicit (auto-) unboxing with lambda expression.
mapToInt(i -> i)
Given a list with a null value
List<Integer> list = Arrays.asList(1, 2, null, 4, 5);
Here are three options to handle null:
Filter out the null values before mapping.
int[] arr = list.parallelStream().filter(Objects::nonNull).mapToInt(Integer::intValue).toArray();
Map the null values to a default value.
int[] arr = list.parallelStream().map(i -> i == null ? -1 : i).mapToInt(Integer::intValue).toArray();
Handle null inside the lambda expression.
int[] arr = list.parallelStream().mapToInt(i -> i == null ? -1 : i.intValue()).toArray();
This simple loop is always correct! no bugs
int[] integers = new int[myList.size()];
for (int i = 0; i < integers.length; i++) {
integers[i] = myList.get(i);
}
I've noticed several uses of for loops, but you don't even need anything inside the loop. I mention this only because the original question was trying to find less verbose code.
int[] toArray(List<Integer> list) {
int[] ret = new int[ list.size() ];
int i = 0;
for( Iterator<Integer> it = list.iterator();
it.hasNext();
ret[i++] = it.next() );
return ret;
}
If Java allowed multiple declarations in a for loop the way C++ does, we could go a step further and do for(int i = 0, Iterator it...
In the end though (this part is just my opinion), if you are going to have a helping function or method to do something for you, just set it up and forget about it. It can be a one-liner or ten; if you'll never look at it again you won't know the difference.
There is really no way of "one-lining" what you are trying to do, because toArray returns an Object[] and you cannot cast from Object[] to int[] or Integer[] to int[].
int[] ret = new int[list.size()];
Iterator<Integer> iter = list.iterator();
for (int i=0; iter.hasNext(); i++) {
ret[i] = iter.next();
}
return ret;
Also try Dollar (check this revision):
import static com.humaorie.dollar.Dollar.*
...
List<Integer> source = ...;
int[] ints = $(source).convert().toIntArray();
With Eclipse Collections, you can do the following if you have a list of type java.util.List<Integer>:
List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int[] ints = LazyIterate.adapt(integers).collectInt(i -> i).toArray();
Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, ints);
If you already have an Eclipse Collections type like MutableList, you can do the following:
MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int[] ints = integers.asLazy().collectInt(i -> i).toArray();
Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, ints);
Note: I am a committer for Eclipse Collections
I would recommend you to use the List<?> skeletal implementation from the Java collections API. It appears to be quite helpful in this particular case:
package mypackage;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Test {
// Helper method to convert int arrays into Lists
static List<Integer> intArrayAsList(final int[] a) {
if(a == null)
throw new NullPointerException();
return new AbstractList<Integer>() {
#Override
public Integer get(int i) {
return a[i]; // Autoboxing
}
#Override
public Integer set(int i, Integer val) {
final int old = a[i];
a[i] = val; // Auto-unboxing
return old; // Autoboxing
}
#Override
public int size() {
return a.length;
}
};
}
public static void main(final String[] args) {
int[] a = {1, 2, 3, 4, 5};
Collections.reverse(intArrayAsList(a));
System.out.println(Arrays.toString(a));
}
}
Beware of boxing/unboxing drawbacks.
Using a lambda you could do this (compiles in JDK lambda):
public static void main(String ars[]) {
TransformService transformService = (inputs) -> {
int[] ints = new int[inputs.size()];
int i = 0;
for (Integer element : inputs) {
ints[ i++ ] = element;
}
return ints;
};
List<Integer> inputs = new ArrayList<Integer>(5) { {add(10); add(10);} };
int[] results = transformService.transform(inputs);
}
public interface TransformService {
int[] transform(List<Integer> inputs);
}