Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why can't byte array be stored in integer array in java

Tags:

java

This code is valid

int h;
byte r;
h=r;

but these are not

int[] h;
byte[] r;
h=r;

or say

int[] h =new byte[4];

I would like to know why?

like image 820
Nav Avatar asked Jul 28 '13 13:07

Nav


People also ask

Can we add byte and int in Java?

The addition of two-byte values in java is the same as normal integer addition. The byte data type is 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive).

Can we convert byte to int in Java?

The intValue() method of Byte class is a built in method in Java which is used to return the value of this Byte object as int.

Can we add byte and int?

operandN type) in this case MAX(int,byte,byte) the max value is int which is maximum so c will have the int value but c has been declared as byte, and we can't store int(bigger) value into byte(smaller). the same applies for every arithmetic operator.


5 Answers

There's an implicit conversion from byte to int, but not from byte[] to int[]. This makes a lot of sense - the JIT compiler knows that to get to a value in an int[], it just needs to multiply the index by 4 and add that to the start of the data (after validation, and assuming no extra padding, of course). That wouldn't work if you could assign a byte[] reference to an int[] variable - the representations are different.

The language could have been designed to allow that conversion but make it create a new int[] which contained a copy of all the bytes, but that would have been pretty surprising in terms of the design of the rest of Java, where the assignment operator just copies a value from the right hand side of the operator to the variable on the left.

Alternatively, we could have imposed a restriction on the VM that every array access would have to look at the actual type of the array object in question, and work out how to get to the element appropriately... but that would have been horrible (even worse than the current nastiness of reference-type array covariance).

like image 98
Jon Skeet Avatar answered Oct 09 '22 16:10

Jon Skeet


That's the design. When you assign byte to wider int, that's okay. But when you declare new byte[4], that's a ["continuous"] part of memory which is, roughly speaking, equal to 4 * 8 bits (or 4 bytes). And one int is 32 bits, so, technically, all your byte array's size is equal to size of one int. In C, where you have a direct memory access, you could do some pointer magic and get your byte pointer casted to int pointer. In Java, you cant and that's safe.

Anyway, why do you want that? Disclaimer: the code below is considered to be extremely unlikely seen anywhere except for the most critical sections in some performance-sensitive libraries/apps. Ideone: http://ideone.com/e14Omr

Comments are explanatory enough, I hope.

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
        }
    }
}
like image 20
tkroman Avatar answered Oct 09 '22 16:10

tkroman


The difference is firstly due to the difference in behavior between primitive types and reference types.

In case you're not familiar with it, primitive types have "value semantics". This means that when you do a = b; when a and b are a primitive type (byte, short, int, long, float, double, boolean, or char) the numeric/boolean value is copied. For example:

int a = 3;
int b = a; // int value of a is copied to b
a = 5;
System.out.println(b); // outputs: 3

But arrays are objects, and objects have "reference semantics". That means that when you do a = b; where a and b are both declared as an array type, the array object that is referred to becomes shared. In a sense the value is still copied, but here the "value" is just the pointer to the object located elsewhere in memory. For example:

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

So it is okay to do int = byte because the numeric value is going to be copied (as they are both primitive types) and also because any possible value of type byte can be safely stored in an int (it is a "widening" primitive conversion).

But int[] and byte[] are both object types, so when you do int[] = byte[] you are asking for the object (the array) to be shared (not copied).

Now you have to ask, why can't an int array and a byte array share their array memory? And what would if mean if they did?

Ints are 4 times the size of bytes, so if the int and byte arrays were to have the same number of elements, then this causes all sorts of nonsense. If you tried to implement it in a memory efficient way, then complex (and very slow) run-time logic would be needed when accessing elements of int arrays to see if they were actually byte arrays. Int reads from byte array memory would have to read and widen the byte value, and int stores would have to either lose the upper 3 bytes, or throw an exception saying that there isn't enough space. Or, you could do it in a fast but memory-wasting way, by padding all byte arrays so that there are 3 wasted bytes per element, just in case somebody wants to use the byte array as an int array.

On the other hand, perhaps you want to pack 4 bytes per int (in this case, the shared array won't have the same number of elements depending on the type of the variable you use to access it). Unfortunately this also causes nonsense. The biggest problem is that it is not portable across CPU architectures. On a little-endian PC, b[0] would refer to the low byte of i[0], but on an ARM device b[0] might point at the high byte of i[0] (and it could even change while the program is running as ARM has a switchable endianness). The overhead of accessing the array's length property would also be made more complicated, and just what should happen if the byte array's length is not divisible by 4?!

You can do this in C, but that's because C arrays don't have a well-defined length property and because C doesn't try to protect you from the other issues. C doesn't care if you go outside the array bounds or muddle up endianness. But Java does care, so it is not feasible to share the array memory in Java. (Java doesn't have unions.)

That's why int[].class and byte[].class both separately extend class Object, but neither class extends the other. You can't store a reference to a byte array in a variable that is declared to point at int arrays, in the same way you can't store a reference to a List in a variable of type String; they're just incompatible classes.

like image 34
Boann Avatar answered Oct 09 '22 16:10

Boann


When you say

int[] arr = new byte[5];

you copy references. On the right hand side is a reference to a byte array. Essentially, this looks like:

|__|__|__|__|__|
0  1  2  3  4            offset of elements, in bytes
^
|
reference to byte array

On the left hand side is a reference to an int array. This, however, is expected to look thus:

|________|________|________|________|________|
0        4        8        12       16
^
|
reference to int array

Hence, simply copying the reference is not possible. For, to get arr[1], the code would look at the starting address+4 (rather than starting adress+1).

The only way to achieve what you want is to create an int[] that has the same number of elements and copy the bytes there.

The rationale behind not doing that automatically:

  • interpreting a single byte as an int comes at essentially no cost, especially no memory must be allocated.
  • copying a byte array is completly different. The new int array must be allocated, which is at least 4 times at big as the byte array. The copy process itself could take some time.

Conclusion: In Java, you can always say "I want to treat this special byte as if it were an int." But you can not say: "I want to treat some data structure (like an array, or a class instance) that contains bytes as if it contained ints."

like image 2
Ingo Avatar answered Oct 09 '22 16:10

Ingo


Simply, Type byte[] does not extend int[]

like image 1
ThiepLV Avatar answered Oct 09 '22 17:10

ThiepLV