将一个对象数组转换为其基本类型的数组

85
如果你有一个Java对象数组,其中包含原始类型(例如Byte、Integer、Char等),是否有一种简便的方法可以将其转换为原始类型的数组?特别地,能否在不创建新数组并循环遍历内容的情况下完成此操作。
例如,给定以下数组:
Integer[] array

最简单的方法是将此内容转换为什么?

int[] intArray

不幸的是,当我们在Hibernate和一些我们无法控制的第三方库之间进行接口操作时,我们必须经常这样做。看起来这应该是一个非常常见的操作,所以如果没有快捷方式,我会感到惊讶。

谢谢你的帮助!

10个回答

93

80

在Java 8中引入了,这可以通过以下方式完成:

int[] intArray = Arrays.stream(array).mapToInt(Integer::intValue).toArray();

然而,目前只有针对 int, longdouble 的原始流。如果您需要转换为另一种原始类型,例如 byte,最简短的方法而不需要外部库是:

byte[] byteArray = new byte[array.length];
for(int i = 0; i < array.length; i++) byteArray[i] = array[i];

如果您希望的话,可以使用流来替换for循环:
IntStream.range(0, array.length).forEach(i -> byteArray[i] = array[i]);

如果您的元素中有任何一个是 null,所有这些操作都会抛出 NullPointerException


2
你可以使用 i -> i(使用拆箱)代替 Integer::intValue - robinst
2
@robinst 而且解包是编译器为您调用Integer:: intValue,那么为什么要创建一个新的lambda,当该方法已经可用? - Andreas
@Andreas 只是列举了另一个选项,选择哪个取决于代码风格/个人偏好。我还使用JMH进行了微基准测试,这两种方法的性能相同。 - robinst
从我的角度来看,Arrays.setAll(unboxed, i -> boxed[i]); 是一个不错的替代方案,可以取代 for 循环。 - Björn Zurmaar
@BjörnZurmaar setAll 只有与流相同的原始类型重载,因此您无法将其用于 byte,但是您可以执行 Arrays.setAll(array, i -> byteArray[i] = array[i]),这也会写入原始数组。 - Alex - GlassEditor.com
显示剩余2条评论

39

很遗憾,在Java平台上没有任何东西可以做到这一点。另外,您还需要明确处理Integer []数组中的null元素(对于它们使用什么int?)。


6
关于 null 值的观点很好。就我的目的而言,如果条目之一为 null,则我会接受抛出异常,就像取消装箱对象时抛出 NullPointerException 一样。 - Il-Bhima
3
这个答案已经不再准确了,针对Java 8,请参考Alex的回答 - robinst

27

3
特别是在不创建新数组并遍历内容的情况下,无法将Integer数组转换为int数组(即无法更改数组元素的类型)。因此,您必须创建一个新的int []数组并将Integer对象的值复制到其中,或者您可以使用适配器:
class IntAdapter {
    private Integer[] array;
    public IntAdapter (Integer[] array) { this.array = array; }
    public int get (int index) { return array[index].intValue(); }
}

这可以使您的代码更易读,而IntAdapter对象只会占用几个字节的内存。适配器的重要优点是您可以在此处处理特殊情况:

class IntAdapter {
    private Integer[] array;
    public int nullValue = 0;
    public IntAdapter (Integer[] array) { this.array = array; }
    public int get (int index) { 
        return array[index] == null ? nullValue : array[index].intValue();
    }
}

另一种解决方案是使用Commons Primitives,其中包含许多预定义的适配器。在您的情况下,请查看ListIntList

2

如果你只需要执行一次,那么可以采用简单的方法。但是你没有提到Integer!=null的情况。

    //array is the Integer array
    int[] array2 = new int[array.length];
    int i=0;
    for (Integer integer : array) {
        array2[i] = integer.intValue();
        i++;
    }

0

我们可以使用Stream API从其装箱对象对应项创建原始类型数组。

对于Character[]数组转换为char[],可以使用相应大小分配的自定义Collector,其中包括Supplier<CharBuffer>BiConsumer<CharBuffer, Character>累加器、BinaryOperator<CharBuffer>组合器和Function<CharBuffer, char[]>完成器,如下所示:

Collector<Character, CharBuffer, char[]> charArrayCollector = Collector.of(
  () -> CharBuffer.allocate(95), 
  CharBuffer::put, 
  CharBuffer::put, 
  CharBuffer::array
);

它为可打印的ASCII字符提供了CharBuffer,将每个流式字符累加到一个CharBuffer实例中,以正确的顺序组合来自多个CharBuffer实例的并行处理结果,并在所有线程完成后最终构建所需的char[]数组。

首先,我们利用IntStreamint值从标准可打印ASCII集创建一个Character[]测试数组,通过迭代ASCII范围并将每个值映射到一个字符Stream中,将它们强制转换为char基元类型,然后将其转换为Character对象:

Character[] asciiCharacters = IntStream.range(32, 127)
  .mapToObj(i -> Character.valueOf((char)i))
  .toArray(Character[]::new);

现在,我们只需要从Character数组创建一个字符流Stream,然后通过自定义Collector将其收集到char[]数组中。
char[] asciiChars = Stream.of(asciiCharacters ).collect(charArrayCollector);

这同样适用于其他Number类型:

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);
byte[] collectedBytes = Stream.of(boxedBytes).collect(
  Collector.of(
    () -> ByteBuffer.allocate(boxedBytes.length), 
    ByteBuffer::put, 
    ByteBuffer::put, 
    ByteBuffer::array
  )
);

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);
short[] collectedShorts = Stream.of(boxedShorts).collect(
  Collector.of(
    () -> ShortBuffer.allocate(boxedShorts .length), 
    ShortBuffer::put, 
    ShortBuffer::put, 
    ShortBuffer::array
  )
);

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);
float[] collectedFloats = Stream.of(boxedFLoats).collect(
  Collector.of(
    () -> FloatBuffer.allocate(boxedFLoats.length), 
    FloatBuffer::put, 
    FloatBuffer::put, 
    FloatBuffer::array
  )
);

原始类型(Stream API支持的),可以更容易地进行转换:

int[] ints = new int[] { Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE };
Integer[] integers = IntStream.of(ints).boxed().toArray(Integer[]::new);
int[] collectedInts = Stream.of(integers).collect(
  Collector.of(
    () -> IntBuffer.allocate(integers.length), 
    IntBuffer::put, 
    IntBuffer::put, 
    IntBuffer::array
  )
);

long[] longs = new long[] { Long.MIN_VALUE, -1l, 0l, 1l, Long.MAX_VALUE };
Long[] boxedLongs = LongStream.of(longs).boxed().toArray(Long[]::new);
long[] collectedLongs = Stream.of(boxedLongs ).collect(
  Collector.of(
    () -> LongBuffer.allocate(boxedLongs .length), 
    LongBuffer::put, 
    LongBuffer::put, 
    LongBuffer::array
  )
);

double[] doubles = new double[] { Double.MIN_VALUE, -1.0, 0, 1.0, Double.MAX_VALUE };
Double[] boxedDoubles = DoubleStream.of(doubles)
  .boxed()
  .toArray(Double[]::new);
double[] collectedDoubles = Stream.of(boxedDoubles).collect(
  Collector.of(
    () -> DoubleBuffer.allocate(boxedDoubles.length), 
    DoubleBuffer::put, 
    DoubleBuffer::put, 
    DoubleBuffer::array
  )
);

0

使用Dollar非常简单:

Integer[] array = ...;
int[] primitiveArray = $(array).toIntArray();

6
看起来这不是Java,至少不是Java 1.6或1.7版本。 - Lordalcol
2
@LorDalCol Dollar实际上是一个Java库。 - Jaroslav Záruba
1
Java方法的名称可以为$!虽然我认为这并不被鼓励…… - Ole V.V.

0

这里是适用于所有基本类型的通用解决方案

/**
 * Convert Collection to equivalent array of primitive type
 * @param <S> [in] Object type of source collection
 * @param tcls [in] class of the primitive element
 * @param q [in] source collection
 * @return Equivalent Array of tcls-elements, requires cast to "tcls"[]
 */
public static <S> Object asPrimitiveArray(Class<?> tcls, Collection<S> q)
{
    int n = q.size();
    Object res = Array.newInstance(tcls, n);
    Iterator<S> i = q.iterator();
    int j = 0;
    while (i.hasNext())
    {
        Array.set(res, j++, i.next());
    }
    return res;
}

/**
 * Convert Object array to equivalent array of primitive type
 * @param <S> [in] Object type of source array
 * @param tcls [in] class of the primitive element
 * @param s [in] source array
 * @return Equivalent Array of tcls-elements, requires cast to "tcls"[]
 */
public static <S> Object asPrimitiveArray(Class<?> tcls, S[] s)
{
    return asPrimitiveArray(tcls, Arrays.asList(s));
} 

将 Integer 转换为 int

Integer[] a = ...
int[] t = (int[]) asPrimitiveArray(int.class, a);

-1

是的,我们可以将对象数组转换为基本类型,请参考下面的代码。 Java 8非常适合学习Java 8技能,我有一个YouTube频道。

https://www.youtube.com/@thefullstackguy 祝学习愉快 :)

private static int[] reverseJava8(int[] arr) {
    Object[] objects = Arrays.stream(arr)
            .boxed()
            .sorted(Comparator.reverseOrder())
            .toArray();
    return Arrays.stream(objects).mapToInt(i -> (int) i).toArray();

}

为什么要进行逆向排序?OP并没有要求这样做。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接