Преобразование массива примитивов в массив контейнеров в Java
Есть ли элегантный способ превратить массив примитивов в массив соответствующих объектов контейнера - например, превратите byte[]
в byte[]
? Или я зациклился на нем и делаю это вручную?
Да, цикл for
не совсем трудный. Просто любопытное уродство.
Ответы
Ответ 1
Apache Commons
Apache Commons/Lang имеет класс ArrayUtils, который определяет эти методы.
- Все методы, называемые
toObject(...)
конвертируют из примитивного массива в массив toObject(...)
- Все
toPrimitive(...)
конвертировать из массива объектов- 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.
Приятно подумать об этих коллекциях с поддержкой массивов, что
- они представляют собой живые представления (т.е. обновления массива меняют список и наоборот)
- объекты-обертки создаются только при необходимости (например, при повторении списка)
См.: Гува Разъяснения/Примитивы
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 работает для int
, long
и double
, но не для byte
, поскольку Arrays.stream() имеет только перегрузки для int[]
, long[]
, double[]
или общего объекта T[]
.
Ответ 2
Чтобы предложить альтернативу, Guava вы можете использовать одну из утилит примитивного типа, например Bytes
или Ints
для создания List
типа оболочки:
byte[] bytes = ...
List<Byte> byteList = Bytes.asList(bytes);
Вместо того, чтобы перебирать и преобразовывать каждый byte
, эти методы фактически создают список, который поддерживается данным массивом. Если вам действительно нужен Byte[]
, это, очевидно, напрямую не дает вам то, что вам нужно (хотя вы можете получить его, используя .toArray(new Byte[bytes.length])
, конечно). Однако коллекции намного превосходят массивы для объектов, и они должны быть предпочтительными, когда это возможно.
Ответ 3
Вам нужно пройти через массив.
Обновлен после ответа @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;
}
И если вы не будете использовать общедоступную библиотеку lib, вам следует просто повторно использовать этот метод и избежать бесполезной зависимости (IMHO).
Ответ 4
Вот короткий общий способ сделать это без использования каких-либо внешних библиотек, и он работает для всех примитивов:
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
После добавления хорошего ответа, здесь ужасный ответ, просто для этого. Что меня беспокоит в классе 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;
}
}
Как вы можете видеть, в этом методе довольно много ошибок:
- Там нет безопасности во время компиляции, параметр метода может быть любым, и только сам метод будет проверять параметры времени выполнения, строго отклоняя нулевые значения, пустые массивы, не-массивы и непримитивные массивы.
- Требуется отражение
- Невозможно поддерживать пустые массивы без сохранения какой-либо таблицы поиска между примитивными и оболочными классами.
В любом случае, вот набор тестов для всех необходимых сценариев, используя 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()));
}
}
}
}