Почему массив байтов не может быть сохранен в целочисленном массиве в java
Этот код действителен
int h;
byte r;
h=r;
но это не
int[] h;
byte[] r;
h=r;
или скажите
int[] h =new byte[4];
Я хотел бы знать, почему?
Ответы
Ответ 1
Разница, во-первых, обусловлена различием в поведении между примитивными типами и ссылочными типами.
Если вы не знакомы с ним, примитивные типы имеют "семантику значений". Это означает, что когда вы выполняете a = b;
, когда a
и b
являются примитивными типами (byte
, short
, int
, long
, float
, double
, boolean
, или char
) скопировано числовое/логическое значение. Например:
int a = 3;
int b = a; // int value of a is copied to b
a = 5;
System.out.println(b); // outputs: 3
Но массивы - это объекты, а объекты имеют "ссылочную семантику". Это означает, что когда вы a = b;
, где a
и b
объявляются как тип массива, объект массива, на который ссылается, становится общим. В некотором смысле значение все еще копируется, но здесь "значение" - это просто указатель на объект, расположенный в другом месте в памяти. Например:
int[] a = new int[] { 3 };
int[] b = a; // pointer value of a is copied to b, so a and b now point at the same array object
a[0] = 5;
System.out.println(b[0]); // outputs: 5
a = null; // note: 'a' now points at no array, although this has no effect on b
System.out.println(b[0]); // outputs: 5
Итак, можно сделать int = byte
, потому что числовое значение будет скопировано (поскольку они оба являются примитивными типами), а также потому, что любое возможное значение байта типа может быть безопасно сохранено в int (это "расширяющееся" примитивное преобразование).
Но int[]
и byte[]
- оба типа объектов, поэтому, когда вы выполняете int[] = byte[]
, вы запрашиваете, чтобы объект (массив) был общим (не копируется).
Теперь вы должны спросить: почему массив int и байтовый массив не разделяют память массива? А что если это будет означать, если бы они это сделали?
Ints в 4 раза больше байтов, поэтому, если массивы int и byte должны иметь одинаковое количество элементов, тогда это вызывает всевозможные глупости. Если вы попытались реализовать его в эффективном режиме памяти, тогда при доступе к элементам массивов int потребуется сложная (и очень медленная) логика времени выполнения, чтобы определить, были ли они фактически байтовыми массивами. Int считывает из памяти массива байтов, чтобы читать и расширять значение байта, а int-магазины должны либо потерять верхние 3 байта, либо выбросить исключение, заявив, что недостаточно места. Или вы могли бы сделать это быстро, но с потерей памяти, заполнив все байтовые массивы, чтобы было 3 потерянных байта на элемент, на всякий случай кто-то хочет использовать массив байтов как массив int.
С другой стороны, возможно, вы хотите упаковать 4 байта на int (в этом случае общий массив не будет иметь одинаковое количество элементов в зависимости от типа переменной, которую вы используете для доступа к ней). К сожалению, это также приводит к бессмыслию. Самая большая проблема заключается в том, что он не переносится по архитектуре процессора. На мало-endian ПК b[0]
будет ссылаться на младший байт i[0]
, но на устройстве ARM b[0]
может указывать на старший байт i[0]
(и он может даже измениться во время работы программы, поскольку ARM имеет переключаемый контур). Накладные расходы на доступ к свойству длины массива также будут усложняться, и только то, что должно произойти, если длина массива байтов не делится на 4?!
Вы можете сделать это на C, но это потому, что массивы C не имеют определенного свойства длины и потому, что C не пытается защитить вас от других проблем. C не волнует, если вы выходите за пределы массива или путаете конспект. Но Java позаботится, поэтому невозможно разделить память массива на Java. (Java не имеет союзов.)
Вот почему int[].class
и byte[].class
оба отдельно расширяют класс Object
, но ни один из них не расширяет другой. Вы не можете сохранить ссылку на массив байтов в переменной, объявленной как точка в массивах int, так же, как вы не можете сохранить ссылку на List
в переменной типа String
; они просто несовместимые классы.
Ответ 2
Там подразумевается преобразование от byte
до int
, но не от byte[]
до int[]
. Это имеет большой смысл - компилятор JIT знает, что для получения значения в int[]
ему просто нужно умножить индекс на 4 и добавить его к началу данных (после проверки и без дополнительного дополнения, конечно). Это не сработает, если вы можете назначить ссылку byte[]
переменной int[]
- представления различны.
Язык мог быть разработан таким образом, чтобы это преобразование, но создало новый int[]
, в котором содержалась копия всех байтов, но это было бы довольно неожиданно с точки зрения дизайна остальной части Java, где оператор присваивания просто копирует значение из правой части оператора в переменную слева.
В качестве альтернативы мы могли бы наложить ограничение на виртуальную машину, чтобы каждый доступ к массиву должен был смотреть на фактический тип рассматриваемого объекта массива и определять, как правильно обращаться к элементу... но это могло бы иметь был ужасен (даже хуже нынешней гадкости ковариации массива ссылочного типа).
Ответ 3
Это дизайн. Когда вы назначаете byte
более широкому int
, это нормально. Но когда вы объявляете new byte[4]
, что [ "непрерывная" ] часть памяти, которая, грубо говоря, равна 4 * 8 бит (или 4 байта). И один int
- 32 бита, поэтому технически весь размер массива byte
равен размеру одного int
. В C, где у вас есть прямой доступ к памяти, вы можете сделать магию указателя и получить указатель byte
, присвоенный указателю int
. В Java вы не можете и не будете в безопасности.
В любом случае, зачем вам это нужно?
Отказ от ответственности: приведенный ниже код считается крайне маловероятным, если вы не найдете места, кроме своей игровой площадки.
Хорошо, я получил свой пример с небезопасной работой. Посмотрите на идеон: http://ideone.com/e14Omr
Комментарии достаточно толковые, надеюсь.
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
/* too lazy to run with VM args, use Reflection */
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
/* get array address */
Unsafe unsafe = (Unsafe)f.get(null);
byte four_bytes[] = {25, 25, 25, 25};
Object trash[] = new Object[] { four_bytes };
long base_offset_bytes = unsafe.arrayBaseOffset(Object[].class);
long four_bytes_address = unsafe.getLong(trash, base_offset_bytes); // <- this is it
long ints_addr = unsafe.allocateMemory(16); // allocate 4 * 4 bytes, i.e. 4 ints
unsafe.copyMemory(four_bytes_address + base_offset_bytes, ints_addr, 4); // copy all four bytes
for(int i = 0; i < 4; i++) {
System.out.println(unsafe.getInt(ints_addr + i)); //run through entire allocated int[],
// get some intestines
}
System.out.println("*****************************");
for(int i = 0; i < 16; i++) {
System.out.println(unsafe.getByte(ints_addr + i)); //run through entire allocated int[],
// get some intestines
}
}
}
Ответ 4
Просто, тип byte [] не расширяет int []
Ответ 5
Когда вы говорите
int[] arr = new byte[5];
вы копируете ссылки. С правой стороны находится ссылка на массив байтов. По существу, это выглядит так:
|__|__|__|__|__|
0 1 2 3 4 offset of elements, in bytes
^
|
reference to byte array
В левой части это ссылка на массив int. Это, однако, должно выглядеть так:
|________|________|________|________|________|
0 4 8 12 16
^
|
reference to int array
Следовательно, простое копирование ссылки невозможно. Ибо, чтобы получить arr [1], код будет смотреть на начальный адрес + 4 (вместо начального адреса + 1).
Единственный способ добиться того, что вы хотите, - создать int [], который имеет одинаковое количество элементов и скопировать туда байты.
Обоснование того, что вы не выполняете это автоматически:
- Интерпретация одного байта как int практически не требует затрат, особенно не выделяется память.
- копирование байтового массива полностью отличается. Необходимо выделить новый массив int, который по крайней мере в 4 раза больше, чем массив байтов. Сам процесс копирования может занять некоторое время.
Заключение. В Java вы всегда можете сказать "Я хочу обработать этот специальный байт, как если бы это был int". Но вы не можете сказать: "Я хочу обрабатывать некоторую структуру данных (например, массив или экземпляр класса), который содержит байты, как если бы он содержал ints."
Ответ 6
вы не можете, потому что его, как большой элемент, будет храниться в меньшем. Невозможно сохранить содержимое в байтах. Это наша конструкция памяти, которая решает этот тип размещения