在Java中将基元数组转换为容器数组

时间:2010-09-22 14:32:10

标签: java arrays

是否有一种优雅的方法可以将基元数组转换为相应容器对象的数组 - 例如,将byte[]转换为Byte[]?或者我是否坚持循环并手动完成它?

是的,for循环并不是很难。只是有点难看。

6 个答案:

答案 0 :(得分:70)

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,它提供所有数字类型的列表,由原始数组支持:

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

很好地想到这些支持数组的集合就是

  1. 它们是实时视图(即对阵列的更新会更改列表,反之亦然)
  2. 仅在需要时创建包装器对象(例如,在迭代List时)
  3. 请参阅:Guava Explained / Primitives


    Java 8

    另一方面,使用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);
    

    请注意,Java 8版本适用于intlongdouble,但不适用于byte,因为Arrays.stream()只有{{}的重载1}},int[]long[]或通用对象double[]

答案 1 :(得分:8)

只是建议一个替代方案,使用Guava,您可以使用其中一个基本类型实用程序(如BytesInts)来创建包装类型的List

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

这些方法实际上创建了一个由给定数组支持的列表,而不是循环并转换每个byte。如果你真的需要一个Byte[],这显然不会直接给你你需要的东西(虽然你当然可以使用.toArray(new Byte[bytes.length])得到它)。但是,集合远远优于对象的数组,并且在可能的情况下应该首选。

答案 2 :(得分:7)

你必须遍历你的数组。


@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,否则你应该简单地重用这个方法并避免无用的依赖(恕我直言)。

答案 3 :(得分:3)

这是一种简单的通用方法,可以在不使用任何外部库的情况下完成它,它适用于所有基元:

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

答案 4 :(得分:2)

是否有一种优雅的方法将原语数组转换为数组 相应的容器对象?

假设您有一个字节数组:

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

答案 5 :(得分:1)

添加a good answer后,这是一个糟糕的答案,只是为了它。让我对Apache Commons ArrayUtils类感到困扰的是,同一方法有8个版本,仅适用于不同的输入类型。我发现了一种将任何原始数组转换为其包装器等效项的通用方法(因此将8个不同的版本减少为1)。这是代码:

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


}