Почему массив байтов не может быть сохранен в целочисленном массиве в 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

вы не можете, потому что его, как большой элемент, будет храниться в меньшем. Невозможно сохранить содержимое в байтах. Это наша конструкция памяти, которая решает этот тип размещения