代码之家  ›  专栏  ›  技术社区  ›  BlairHippo

Java中原语数组到容器数组的转换

  •  43
  • BlairHippo  · 技术社区  · 15 年前

    有没有一种优雅的方法将一组基本体转换为相应容器对象的数组——将 byte[] 变成一个 Byte[] 例如?还是我一直在循环和手动操作?

    是的,那个 for 循环并不困难。只是有点难看。

    5 回复  |  直到 15 年前
        1
  •  86
  •   Sean Patrick Floyd    8 年前

    阿帕奇公地

    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 相反,它提供了所有数值类型的列表,并由基元数组支持:

    List<Integer> intList = Ints.asList(1,2,3,4,5);
    List<Long> longList   = Longs.asList(1L,2L,3L,4L,5L);
    // etc.
    

    这些阵列支持的集合的好处是

    1. 包装器对象仅在需要时创建(例如,在迭代列表时)

    请参见: Guava Explained / Primitives


    另一方面,使用Java 8 lambdas/streams,您可以使这些转换非常简单,而无需使用外部库:

    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);
    

    请注意,Java8版本适用于 int , long double byte ,作为数组.stream()仅具有的重载 int[] long[] , double[] 或者泛型对象 T[] .

        2
  •  9
  •   Colin Hebert    15 年前


    基本上 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,否则您应该简单地重用这个方法并避免无用的依赖关系(IMHO)。

        3
  •  8
  •   ColinD    15 年前

    只是想提出一个替代方案 Guava 可以使用基本类型实用程序之一,例如 Bytes Ints 创建 List 包装类型的名称:

    byte[] bytes = ...
    List<Byte> byteList = Bytes.asList(bytes);
    

    而不是循环和转换每一个 byte ,这些方法实际上创建了一个由给定数组支持的列表。如果你真的需要 Byte[] .toArray(new Byte[bytes.length]) 当然)。不过,对于对象而言,集合远远优于数组,如果可能的话,应该首选集合。

        4
  •  3
  •   pathikrit    11 年前

    这是一种不使用任何外部库的简单通用方法,适用于所有原语:

    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;
      }
    }
    
        5
  •  2
  •   John McClane    6 年前

    有没有一种优雅的方法可以将一个基本体数组转换成一个数组 对应的容器对象?

    假设您有一个字节数组:

    byte[] b = new byte[20];
    ... (fill b) ...
    

    那你可以用 Arrays.setAll(..) 要转换它:

    Byte[] w = new Byte[b.length];
    Arrays.setAll(w, i -> b[i]);
    

    Arrays.parallelSetAll(...) 速度更快:

    Arrays.parallelSetAll(w, i -> b[i]);
    

    System.out.println(b.getClass().getCanonicalName());
    System.out.println(Arrays.toString(b));
    System.out.println(w.getClass().getCanonicalName());
    System.out.println(Arrays.toString(w));
    

    如果您需要各种基本数组的通用包装器,请参见:

    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;
    }
    

    像这样使用:

    Byte[] w = (Byte[])wrap(b);
    
        6
  •  1
  •   Community Mohan Dere    9 年前

    a good answer

    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;
        }
    
    }
    

    如你所见,这种方法有很多错误:

    • 没有编译时安全性,方法参数可以是任何东西,只有方法本身会验证运行时参数,严格拒绝空值、空数组、非数组和非原语数组
    • 需要反思
    • 如果不在原语类和包装类之间保留某种查找表,就无法支持空数组。

    无论如何,这里有一个测试套件,用于所有必要的场景,使用JUnit的 Parameterized

    @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()));
                }
            }
        }
    
    
    }