在Java中将原始数组转换为容器数组
有没有一种优雅的方式来将一个基元数组转换成相应容器对象的数组 – 例如,将一个byte[]
变成Byte[]
? 还是我坚持循环,手动做?
是的, for
循环不是很难。 只是有点丑。
Apache Commons
Apache Commons / Lang有一个定义这些方法的类ArrayUtils 。
- 所有的方法调用
toObject(...)
从原始数组转换为包装数组 - 所有调用
toPrimitive(...)
从包装对象数组转换为原始数组
例:
final int[] original = new int[] { 1, 2, 3 }; final Integer[] wrappers = ArrayUtils.toObject(original); final int[] primitivesAgain = ArrayUtils.toPrimitive(wrappers); assert Arrays.equals(original, primitivesAgain);
番石榴
但是接下来我会说,包装原语的数组并不是非常有用,所以你可能想看看Guava ,它提供了所有数字types的列表,这些列表由原始数组支持:
List<Integer> intList = Ints.asList(1,2,3,4,5); List<Long> longList = Longs.asList(1L,2L,3L,4L,5L); // etc.
好的想想这些数组支持的集合是
- 他们是实时的意见(即更新arrays改变列表,反之亦然)
- 包装对象只在需要的时候被创build(例如迭代List时)
请参阅: 番石榴解释/基元
Java 8
另一方面,使用Java 8 lambdaexpression式/stream,您可以使这些转换非常简单,而无需使用外部库:
int[] primitiveInts = {1, 2, 3}; Integer[] wrappedInts = Arrays.stream(primitiveInts) .boxed() .toArray(Integer[]::new); int[] unwrappedInts = Arrays.stream(wrappedInts) .mapToInt(Integer::intValue) .toArray(); assertArrayEquals(primitiveInts, unwrappedInts); double[] primitiveDoubles = {1.1d, 2.2d, 3.3d}; Double[] wrappedDoubles = Arrays.stream(primitiveDoubles) .boxed() .toArray(Double[]::new); double[] unwrappedDoubles = Arrays.stream(wrappedDoubles) .mapToDouble(Double::doubleValue) .toArray(); assertArrayEquals(primitiveDoubles, unwrappedDoubles, 0.0001d);
请注意,Java 8版本适用于int
, long
和double
,但不适用于byte
,因为Arrays.stream()仅对int[]
, long[]
, double[]
或通用对象T[]
具有重载。
你必须遍历你的数组。
@seanizer之后更新回答:
基本上, toObject(byte[] array)
方法将为您执行循环:
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; }
除非你真的使用commons lang lib,你应该简单的重用这个方法,避免无用的依赖(恕我直言)。
只是build议一个替代scheme,用番石榴,你可以使用一个基本types的工具,如Bytes
或Ints
来创build一个包装typesList
:
byte[] bytes = ... List<Byte> byteList = Bytes.asList(bytes);
这些方法实际上是创build一个由给定数组支持的列表,而不是循环和转换每个byte
。 如果你真的需要一个Byte[]
,这显然不会直接给你你所需要的(尽pipe你可以使用.toArray(new Byte[bytes.length])
当然)。 虽然集合远远优于数组,但是在可能的情况下应该首选。
这是一个简单的通用方法,不需要使用任何外部库,它适用于所有原语:
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; } }
在添加一个好的答案之后 ,这是一个可怕的答案,仅仅是为了它。 Apache Commons ArrayUtils类令我困扰的是,只有不同的inputtypes,有8个版本的相同的方法。 我find了一个通用的方法来将任何原始数组转换为其包装等效(因此将8个不同的版本减less到一个)。 这是代码:
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; } }
正如你所看到的那样,这个方法有很多错误:
- 没有编译时的安全性,方法参数可以是任何东西,只有方法本身会validation运行时参数,严格地拒绝空值,空数组,非数组和非基元数组
- 反思是需要的
- 如果不在原始类和包装类之间保留某种查找表,就无法支持空数组。
无论如何,这里是所有必要场景的testing套件,使用JUnit的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())); } } } }