在 Java 中将基元数组转换为容器数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3770289/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Converting Array of Primitives to Array of Containers in Java
提问by BlairHippo
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?
有没有一种优雅的方法可以将原始数组转换为相应容器对象的数组——例如,将 abyte[]
转换为 a Byte[]
?还是我一直在循环遍历它并手动执行它?
Yeah, the for
loop isn't exactly difficult. Just kinda ugly.
是的,for
循环并不难。就是有点丑
采纳答案by Sean Patrick Floyd
Apache Commons
阿帕奇公地
Apache Commons / Langhas a class ArrayUtilsthat defines these methods.
Apache Commons / Lang有一个类ArrayUtils来定义这些方法。
- All methods called
toObject(...)
convert from primitive array to wrapper array - All called
toPrimitive(...)
convert from wrapper object array to primitive array
- 调用的所有方法
toObject(...)
从原始数组转换为包装器数组 - 所有调用都
toPrimitive(...)
从包装对象数组转换为原始数组
Example:
例子:
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
番石榴
But then I'd say that Arrays of wrapped primitives are not very useful, so you might want to have a look at Guavainstead, which provides Lists of all numeric types, backed by primitive arrays:
但是我会说包装基元的数组不是很有用,所以你可能想看看番石榴,它提供了所有数字类型的列表,由基元数组支持:
List<Integer> intList = Ints.asList(1,2,3,4,5);
List<Long> longList = Longs.asList(1L,2L,3L,4L,5L);
// etc.
The nice think about these array-backed collections is that
关于这些数组支持的集合的好想法是
- they are live views (i.e. updates to the array change the list and vice-versa)
- the wrapper objects are only created when needed (e.g. when iterating the List)
- 它们是实时视图(即对数组的更新更改列表,反之亦然)
- 包装对象仅在需要时创建(例如,在迭代列表时)
See: Guava Explained / Primitives
请参阅:番石榴解释/原语
Java 8
爪哇 8
On the other hand, with Java 8 lambdas / streams, you can make these conversions pretty simple without using external libraries:
另一方面,使用 Java 8 lambdas / 流,您可以在不使用外部库的情况下使这些转换变得非常简单:
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);
Note that the Java 8 version works for int
, long
and double
, but not for byte
, as Arrays.stream() only has overloads for int[]
, long[]
, double[]
or a generic object T[]
.
请注意,在Java 8版本适用int
,long
并且double
,而不是byte
作为Arrays.stream()只具有过载int[]
,long[]
,double[]
或者一个通用的对象T[]
。
回答by Colin Hebert
You have to loop through your array.
你必须遍历你的数组。
Updated after @seanizer answer :
在@seanizer 回答后更新:
Basically the toObject(byte[] array)
method will do the looping for you :
基本上该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;
}
And unless you will really use the commons lang lib, you should simply reuse this method and avoid a useless dependency (IMHO).
除非你真的会使用公共语言库,否则你应该简单地重用这个方法并避免无用的依赖(恕我直言)。
回答by ColinD
Just to suggest an alternative, with Guavayou can use one of the primitive type utilities such as Bytes
or Ints
to create a List
of the wrapper type:
只是建议一个替代方案,使用Guava,您可以使用原始类型实用程序之一,例如Bytes
或Ints
创建List
包装器类型:
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.
byte
这些方法实际上创建了一个由给定数组支持的列表,而不是遍历和转换 each 。如果你真的需要一个Byte[]
,这显然不会直接给你你需要的东西(当然你可以使用它.toArray(new Byte[bytes.length])
)。不过,集合远优于对象数组,并且在可能的情况下应该是首选。
回答by Sean Patrick Floyd
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:
添加一个好的答案后,这是一个糟糕的答案,只是为了它。Apache Commons ArrayUtils 类让我烦恼的是,相同方法有 8 个版本,只是针对不同的输入类型。我找到了一种将任何原始数组转换为其等效包装器的通用方法(因此将 8 个不同的版本减少为一个)。这是代码:
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:
无论如何,这里有一个适用于所有必要场景的测试套件,使用 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()));
}
}
}
}
回答by pathikrit
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;
}
}