'Converting Array of Primitives to Array of Containers in Java
Is there an elegant way to turn an array of primitives into an array of the corresponding container objects -- turn a byte[] into a Byte[], for example? Or am I stuck with looping through it and doing it manually?
Yeah, the for loop isn't exactly difficult. Just kinda ugly.
Solution 1:[1]
You have to loop through your array.
Updated after @seanizer answer :
Basically the toObject(byte[] array) method will do the looping for you :
public static Byte[] toObject(byte[] array) {
if (array == null) {
return null;
} else if (array.length == 0) {
return EMPTY_BYTE_OBJECT_ARRAY;
}
final Byte[] result = new Byte[array.length];
for (int i = 0; i < array.length; i++) {
result[i] = new Byte(array[i]);
}
return result;
}
And unless you will really use the commons lang lib, you should simply reuse this method and avoid a useless dependency (IMHO).
Solution 2:[2]
Just to suggest an alternative, with Guava you can use one of the primitive type utilities such as Bytes or Ints to create a List of the wrapper type:
byte[] bytes = ...
List<Byte> byteList = Bytes.asList(bytes);
Rather than looping through and converting each byte, these methods actually create a list that is backed by the given array. If you really need a Byte[], this obviously doesn't directly give you what you need (though you can get it using .toArray(new Byte[bytes.length]) of course). Collections are vastly superior to arrays for objects, though, and should be preferred when possible.
Solution 3:[3]
Here is a short generic way of doing it without using any external libraries and it works for all primitives:
import static java.lang.reflect.Array.*;
import java.util.Arrays;
public class DeepConverter {
public static void main(String args[]) {
long L1[][][] = {{{1,2},{3,4}}, {{5,6}}, {{7}},{{8,9,10,11}}};
L1 = new long[2][0][7];
Long L2[][] = (Long[][])box(L1);
System.out.println(Arrays.deepToString(L2));
}
public static Object box(Object src) {
try {
int length = src.getClass().isArray() ? getLength(src) : 0;
if(length == 0)
return src;
Object dest = newInstance(typeCastTo(wrap(get(src, 0))), length);
for(int i = 0; i < length; i++)
set(dest, i, wrap(get(src, i)));
return dest;
} catch(Exception e) {
throw new ClassCastException("Object to wrap must be an array of primitives with no 0 dimensions");
}
}
private static Class<?> typeCastTo(Object obj) {
Class<?> type = obj.getClass();
if(type.equals(boolean.class)) return Boolean.class;
if(type.equals(byte.class)) return Byte.class;
if(type.equals(char.class)) return Character.class;
if(type.equals(double.class)) return Double.class;
if(type.equals(float.class)) return Float.class;
if(type.equals(int.class)) return Integer.class;
if(type.equals(long.class)) return Long.class;
if(type.equals(short.class)) return Short.class;
if(type.equals(void.class)) return Void.class;
return type;
}
}
Solution 4:[4]
Is there an elegant way to turn an array of primitives into an array of the corresponding container objects?
Suppose you have an array of bytes:
byte[] b = new byte[20];
... (fill b) ...
Then you can use Arrays.setAll(..) to convert it:
Byte[] w = new Byte[b.length];
Arrays.setAll(w, i -> b[i]);
Arrays.parallelSetAll(...) is even faster:
Arrays.parallelSetAll(w, i -> b[i]);
To verify the result:
System.out.println(b.getClass().getCanonicalName());
System.out.println(Arrays.toString(b));
System.out.println(w.getClass().getCanonicalName());
System.out.println(Arrays.toString(w));
If you need a universal wrapper for all kinds of primitive arrays, here it is:
public static Object[] wrap(Object a) {
if (a == null)
return null;
int length = Array.getLength(a);
Object b = length > 0 ? a : Array.newInstance(a.getClass().getComponentType(), 1);
Object[] result = (Object[])Array.newInstance(Array.get(b, 0).getClass(), length);
Arrays.parallelSetAll(result, i -> Array.get(a, i));
return result;
}
Use it like this:
Byte[] w = (Byte[])wrap(b);
Solution 5:[5]
After adding a good answer, here's an awful answer, just for the heck of it. What bothers me about the Apache Commons ArrayUtils class is that there are 8 versions of the same method, just for different input types. I found a generic way to convert any primitive array into its wrapper equivalent (hence reducing the 8 different versions to one). This is the code:
public final class ArraysUtils {
private ArraysUtils() { }
@SuppressWarnings("unchecked")
public static Object[] toWrapperArray(final Object primitiveArray) {
Objects.requireNonNull(primitiveArray, "Null values are not supported");
final Class<?> cls = primitiveArray.getClass();
if (!cls.isArray() || !cls.getComponentType().isPrimitive()) {
throw new IllegalArgumentException(
"Only primitive arrays are supported");
}
final int length = Array.getLength(primitiveArray);
if (length == 0) {
throw new IllegalArgumentException(
"Only non-empty primitive arrays are supported");
}
final Object first = Array.get(primitiveArray, 0);
Object[] arr = (Object[]) Array.newInstance(first.getClass(), length);
arr[0] = first;
for (int i = 1; i < length; i++) {
arr[i] = Array.get(primitiveArray, i);
}
return arr;
}
}
As you can see, there's quite a lot wrong with that method:
- There's no compile-time safety, the method parameter can be anything and only the method itself will validate runtime parameters, rigorously rejecting null values, empty arrays, non-arrays and non-primitive arrays
- Reflection was needed
- There is no way to support empty arrays without keeping some sort of lookup table between primitive and wrapper classes.
Anyway, here is a test suite for all the necessary scenarios, using JUnit's Parameterized runner:
@RunWith(Parameterized.class)
public class ArraysUtilsTest {
@Parameterized.Parameters(name = "{0}")
public static List<Object> parameters() {
return Arrays.asList(
success(new int[]{1, 2, 3}, new Integer[]{1, 2, 3}),
success(new long[]{1L, 2L, 3L}, new Long[]{1L, 2L, 3L}),
success(new byte[]{1, 2, 3}, new Byte[]{1, 2, 3}),
success(new short[]{1, 2, 3}, new Short[]{1, 2, 3}),
success(new char[]{'a', 'b', 'c'}, new Character[]{'a', 'b', 'c'}),
success(new double[]{1.0, 2.0, 3.0}, new Double[]{1.0, 2.0, 3.0}),
success(new float[]{1.0f, 2.0f, 3.0f}, new Float[]{1.0f, 2.0f, 3.0f}),
success(new boolean[]{true, false, true}, new Boolean[]{true, false, true}),
failure(null, NullPointerException.class, "Null"),
failure("foo", IllegalArgumentException.class, "Non-array"),
failure(new String[]{"foo", "bar"}, IllegalArgumentException.class, "Non-primitive array"),
failure(new int[0], IllegalArgumentException.class, "Empty array")
);
}
private static Object[] success(Object primitiveArray, Object[] wrapperArray) {
return new Object[]{
primitiveArray.getClass().getCanonicalName(),
primitiveArray, null, wrapperArray};
}
private static Object[] failure(Object input,
Class<? extends RuntimeException> exceptionClass,
String description) {
return new Object[]{description, input, exceptionClass, null};
}
@Parameterized.Parameter(0)
// only used to generate the test name
public String scenarioName;
@Parameterized.Parameter(1)
public Object inputArray;
@Parameterized.Parameter(2)
public Class<? extends RuntimeException> expectedException;
@Parameterized.Parameter(3)
public Object[] expectedOutput;
@Test
public void runScenario() {
try {
Object[] wrapped = ArraysUtils.toWrapperArray(inputArray);
if (expectedException != null) {
fail(String.format("Expected %s to be thrown",
expectedException.getSimpleName()));
}
assertThat(wrapped, is(equalTo(expectedOutput)));
} catch (RuntimeException e) {
if (expectedException == null) {
fail(String.format("Expected no exception but got %swith message '%s'",
e.getClass().getSimpleName(),
e.getMessage()));
}
if(!expectedException.isInstance(e)){
fail(String.format("Expected %s but got %s with message '%s'",
expectedException.getSimpleName(),
e.getClass().getSimpleName(),
e.getMessage()));
}
}
}
}
Solution 6:[6]
To convert a char[] to a Character[] array, we're first going to build a representative test ASCII char array from an IntStream, by using an accordingly size-allocated CharBuffer, inside a custom Collector (built from Supplier, Accumulator, Combiner and finisher Function):
Collector<Character, CharBuffer, char[]> charArrayCollector = Collector.of(
() -> CharBuffer.allocate(95),
CharBuffer::put,
CharBuffer::put,
CharBuffer::array
);
Then, we can create an array from the standard printable ASCII range. Using .range() instead of .iterate() saves the need of limiting the resulting stream afterwards.
The iterated int values are cast to char each and mapped to a 'boxed' Character object stream, which is then collected into the test char[] array:
char[] asciiChars = IntStream.range(32, 127)
.mapToObj(i -> (char)i)
.collect(charArrayCollector);
Now, we can convert the ASCII char[] array, again using an IntStream. This surrogates a classic for loop, by iterating over the array's indices, mapping each contained char into stream of 'boxed' Character objects, finally creating the desired Character[] array in the terminal operation. Stream.iterate is just for demonstration of an alternative way:
Character[] characters = IntStream.range(0, asciiChars.length)
.mapToObj(i -> Character.valueOf(chars[i]))
.toArray(Character[]::new);
Character[] characters2 = Stream.iterate(0, i -> i += 1)
.map(i -> asciiChars[i])
.limit(asciiChars.length)
.toArray(Character[]::new);
Edit:
Other primitive type arrays can be converted similarly; here, the boxing occurs in the .mapToObj() intermediate operation, where the source array elements are extracted and mapped to their object counterparts:
byte[] bytes = new byte[] { Byte.MIN_VALUE, -1 , 0, 1, Byte.MAX_VALUE };
Byte[] boxedBytes = IntStream.range(0, bytes.length)
.mapToObj(i -> bytes[i])
.toArray(Byte[]::new);
short[] shorts = new short[] { Short.MIN_VALUE, -1, 0, 1, Short.MAX_VALUE };
Short[] boxedShorts = IntStream.range(0, shorts.length)
.mapToObj(i -> shorts[i])
.toArray(Short[]::new);
float[] floats = new float[] { Float.MIN_VALUE, -1.0f, 0f, 1.0f, Float.MAX_VALUE };
Float[] boxedFLoats = IntStream.range(0, floats.length)
.mapToObj(i -> floats[i])
.toArray(Float[]::new);
For primitive values supported by the Stream API, the corresponding dedicated stream implementations and .boxed()can be used instead:
int[] ints = new int[] { Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE };
Integer[] integers = IntStream.of(ints)
.boxed()
.toArray(Integer[]::new);
long[] longs = new long[] { Long.MIN_VALUE, -1l, 0l, 1l, Long.MAX_VALUE };
Long[] boxedLongs = LongStream.of(longs)
.boxed()
.toArray(Long[]::new);
double[] doubles = new double[] { Double.MIN_VALUE, -1.0, 0, 1.0, Double.MAX_VALUE };
Double[] boxedDoubles = DoubleStream.of(doubles)
.boxed()
.toArray(Double[]::new);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | ColinD |
| Solution 3 | |
| Solution 4 | John McClane |
| Solution 5 | Community |
| Solution 6 |
