What is the logic of Arrays.copyOfRange (byte [], int, int) weird behavior?

Can someone explain to me the logic of the strange behavior of Arrays.copyOfRange (byte [], int, int)) ? I can illustrate what I mean with a simple example:

byte[] bytes = new byte[] {1, 1, 1}; Arrays.copyOfRange(bytes, 3, 4); // Returns single element (0) array Arrays.copyOfRange(bytes, 4, 5); // Throws ArrayIndexOutOfBoundsException 

In both cases, I copied the ranges outside the bounds of the array (i.e. start >= array.length ), so the condition for the error is at least weird for me (if from < 0 or from > original.length ). In my opinion, it should be: if from < 0 or from >= original.length . Maybe I missed something?

+7
java arrays standard-library
source share
4 answers

JavaDoc defines three points about the expected arguments:

One:

[ of ] must be between zero and original.length , inclusive

Two:

[ to ] must be greater than or equal to

Three

[ to ] may be more original.length

For the case of Arrays.copyOfRange(bytes, 3, 4) one, two, and three are true, which means they are valid.

For the case of Arrays.copyOfRange(bytes, 4, 5) only two and three are true, which means that they are invalid.


Expected Behavior? Yes.

Intuitive behavior? Kinda

If your question is secretly, “why was it designed that way?” then no one can tell you the answer, except the authors of the code.

+1
source share

See how copyOfRange works:

 public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy; } 

The most important part:

 System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); 

So, when you call Arrays.copyOfRange (bytes, 3, 4); last parameter System.arraycopy "length - the number of array elements to be copied" is 0. The call to arraycopy looks like System.arraycopy (original, from, copy, 0,0);

0
source share

Having examined the source of Arrays.copyOfRange (byte [] original, int from, int to)

  public static byte[] copyOfRange(byte[] original, int from, int to) { 1 int newLength = to - from; 2 if (newLength < 0) 3 throw new IllegalArgumentException(from + " > " + to); 4 byte[] copy = new byte[newLength]; 5 System.arraycopy(original, from, copy, 0, 6 Math.min(original.length - from, newLength)); 7 return copy; } 
  • in line 1, the length of the target array is calculated using 1
  • in line 4, a new array is created with a size of 1 , all elements are 0
  • in line 6, the length of the item to be copied is calculated as zero, since

     // Math.min(original.length - from, newLength)); Math.min(3 - 3, 1)); --> returns zero 
  • in line 5, System.arraycopy just doesn't copy anything to the copy array

If your from is equal to bytes.length + 1 , then length gets a negative result (bytes.length - from).

A small code to demonstrate

 public class ArrayCopy { public static void main(String[] args) { byte[] bytes = new byte[]{11, 12, 13}; int from = 3; int to = 4; copyOfRange(bytes, from, to); from = 2; to = 3; copyOfRange(bytes, from, to); from = 4; to = 5; copyOfRange(bytes, from, to); } static void copyOfRange(byte[] bytes, int from, int to) { System.out.printf("%ncopyOfRange(bytes: %s from: %d to: %d)%n", Arrays.toString(bytes), from, to ); // line 1 int newLength = to - from; System.out.println("int newLength = " + newLength); // line 2 if (newLength < 0) { throw new IllegalArgumentException(from + " > " + to); } // line 4 byte[] copy = new byte[newLength]; // to show that in the suspicious case System.arrayCopy does nothing copy[0] = 42; System.out.println("byte[] copy = " + Arrays.toString(copy)); int length = bytes.length - from; System.out.println("int length = " + length); int minLenght = Math.min(length, newLength); System.out.println("int minLenght = " + minLenght); // line 5 System.arraycopy(bytes, from, copy, 0, minLenght); System.out.println("byte[] copy = " + Arrays.toString(copy)); } } 

Exit

 copyOfRange(bytes: [11, 12, 13] from: 3 to: 4) int newLength = 1 byte[] copy = [42] int length = 0 int minLenght = 0 byte[] copy = [42] copyOfRange(bytes: [11, 12, 13] from: 2 to: 3) int newLength = 1 byte[] copy = [42] int length = 1 int minLenght = 1 byte[] copy = [13] copyOfRange(bytes: [11, 12, 13] from: 4 to: 5) int newLength = 1 byte[] copy = [42] int length = -1 int minLenght = -1 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException 
0
source share

This means that you can create a new "empty" array of any length using this method, as described for the situation // Returns single element (0) array .

The main goal why the Arrays.copyOfRange method Arrays.copyOfRange is that it creates a new array on the fly. And this new array may be larger than the original array, of course.

This behavior is a documented function.

The end index of the range (to) , which must be greater than or equal to, can be greater than original.length , in which case '\ u000' is placed in all elements of the copy whose index is greater than or equal to the original .length - from. The length of the returned array will be - from.

Why is this implemented like this? Assuming you will reference this:

 byte[] bytes = new byte[] {1, 1, 1}; byte[] newBytes = Arrays.copyOfRange(bytes, 2, 6); // Returns array length 6 - 2 = 4 

It will create an array [1, 0, 0, 0], and all elements that do not exist in the original array will be initialized with a default literal for the current type. But , if you want to specify from more, but not equal bytes.length (which is discouraged by the documentation), this will ArrayIndexOutOfBoundsException while calling this System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); because original.length - from will be less than 0 .

So technically speaking, if you use such instructions:

 int n = 4; Arrays.copyOfRange(bytes, bytes.length, bytes.length + n); 

assumes you just want to create a new empty array of size n . In other words, you create a new array and do not copy anything from the original array.

0
source share

All Articles