184

I'm looking for a method in Java that will return a segment of an array. An example would be to get the byte array containing the 4th and 5th bytes of a byte array. I don't want to have to create a new byte array in the heap memory just to do that. Right now I have the following code:

doSomethingWithTwoBytes(byte[] twoByteArray);

void someMethod(byte[] bigArray)
{
      byte[] x = {bigArray[4], bigArray[5]};
      doSomethingWithTwoBytes(x);
}

I'd like to know if there was a way to just do doSomething(bigArray.getSubArray(4, 2)) where 4 is the offset and 2 is the length, for example.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
jbu
  • 15,831
  • 29
  • 82
  • 105

15 Answers15

187

Disclaimer: This answer does not conform to the constraints of the question:

I don't want to have to create a new byte array in the heap memory just to do that.

(Honestly, I feel my answer is worthy of deletion. The answer by @unique72 is correct. Imma let this edit sit for a bit and then I shall delete this answer.)


I don't know of a way to do this directly with arrays without additional heap allocation, but the other answers using a sub-list wrapper have additional allocation for the wrapper only – but not the array – which would be useful in the case of a large array.

That said, if one is looking for brevity, the utility method Arrays.copyOfRange() was introduced in Java 6 (late 2006?):

byte [] a = new byte [] {0, 1, 2, 3, 4, 5, 6, 7};

// get a[4], a[5]

byte [] subArray = Arrays.copyOfRange(a, 4, 6);
David J. Liszewski
  • 10,959
  • 6
  • 44
  • 57
  • 12
    this still dynamically allocates a new memory segment and copies the range into that. – Dan Jun 03 '11 at 01:38
  • 4
    Thanks Dan - I neglected that OP didn't want to create new array and I didn't look at implementation of `copyOfRange`. If it were closed-source maybe it could have passed. :) – David J. Liszewski Jun 09 '11 at 14:30
  • 7
    I think a lot of people want to create a sub array from an array and aren't to worried that it uses some more memory. They come across this question and get the answer they want - so please don't delete as it's useful - I think that's ok. – The Lonely Coder Apr 03 '15 at 11:39
  • 2
    actually, copyOfRange still allocate new memory segment – Kevingo Tsai Jun 04 '15 at 06:37
  • 1
    @DavidJ.Liszewski I think its been almost 6 years now since your edit :) – Gurwinder Singh Jul 31 '20 at 08:27
  • @GurwinderSingh Oh jeez, you are right. For better or worse, I now don't pay attention to StackOverflow much at all. @ https://stackoverflow.com/users/1201038/the-lonely-coder . . please create new question with Arrays.copyOfRange as your solution. – David J. Liszewski Feb 05 '21 at 06:04
172

Arrays.asList(myArray) delegates to new ArrayList(myArray), which doesn't copy the array but just stores the reference. Using List.subList(start, end) after that makes a SubList which just references the original list (which still just references the array). No copying of the array or its contents, just wrapper creation, and all lists involved are backed by the original array. (I thought it'd be heavier.)

unique72
  • 1,739
  • 1
  • 10
  • 2
  • 10
    To clarify, it delegates to a private class in `Arrays` confusingly called `ArrayList`, but which really is a `List` around an array, as opposed to `java.util.ArrayList` which would make a copy. No new allocations (of the list's contents), and no third party dependencies. This is, I believe, the most correct answer. – dimo414 Mar 02 '13 at 17:49
  • 32
    Actually, this won't work for primitive type arrays as the OP wanted (`byte[]` in his case). All you'll get would be `List`. And changing `byte[] bigArray` to `Byte[] bigArray` might impose a significant memory overhead. – Dmitry Avtonomov Apr 02 '14 at 03:43
  • 2
    The only way to truly achieve what is desired is through the `sun.misc.Unsafe` class. – Dmitry Avtonomov Apr 02 '14 at 04:04
  • Can one create an ArrayList that directly references a MemorySegment ( native buffer ? ) or a java array that likewise does so ? – peterk Aug 22 '23 at 19:46
40

If you're seeking a pointer style aliasing approach, so that you don't even need to allocate space and copy the data then I believe you're out of luck.

System.arraycopy() will copy from your source to destination, and efficiency is claimed for this utility. You do need to allocate the destination array.

Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
djna
  • 54,992
  • 14
  • 74
  • 117
  • 3
    yes, I was hoping for some kind of pointer method since I don't want to be dynamically allocating memory. but it looks like that's what i'm going to have to do. – jbu Jul 08 '09 at 20:43
  • 1
    As @unique72 suggests, there appear to be ways of doing what you want by exploiting subtleties in the implementation of various java list/array types. This seems to be possible, just not in an explicit manner and that makes me hesitant to rely on it too much... – Andrew Nov 16 '16 at 01:32
  • Why should `array*copy*()` reuse the same memory? Isn't that the exact opposite what a caller would expect? – Patrick Oct 31 '17 at 15:47
24

One way is to wrap the array in java.nio.ByteBuffer, use the absolute put/get functions, and slice the buffer to work on a subarray.

For instance:

doSomething(ByteBuffer twoBytes) {
    byte b1 = twoBytes.get(0);
    byte b2 = twoBytes.get(1);
    ...
}

void someMethod(byte[] bigArray) {
      int offset = 4;
      int length = 2;
      doSomething(ByteBuffer.wrap(bigArray, offset, length).slice());
}

Note that you have to call both wrap() and slice(), since wrap() by itself only affects the relative put/get functions, not the absolute ones.

ByteBuffer can be a bit tricky to understand, but is most likely efficiently implemented, and well worth learning.

Lii
  • 11,553
  • 8
  • 64
  • 88
Soulman
  • 2,910
  • 24
  • 21
  • 1
    It's also worth noting that ByteBuffer objects can be fairly easily decoded: `StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buffer, 0, readBytes))` – skeryl Dec 28 '15 at 21:15
  • @Soulman thanks for the explanation, but one question is it more efficient than using `Arrays.copyOfRange` ? – ucMedia Feb 14 '20 at 16:22
  • 1
    @ucMedia for a two-byte array, `Arrays.copyOfRange` is probably more efficient. Generally, you would have to measure for your specific use case. – Soulman Feb 16 '20 at 21:12
20

Use java.nio.Buffer's. It's a lightweight wrapper for buffers of various primitive types and helps manage slicing, position, conversion, byte ordering, etc.

If your bytes originate from a Stream, the NIO Buffers can use "direct mode" which creates a buffer backed by native resources. This can improve performance in a lot of cases.

James Schek
  • 17,844
  • 7
  • 51
  • 64
14

You could use the ArrayUtils.subarray in apache commons. Not perfect but a bit more intuitive than System.arraycopy. The downside is that it does introduce another dependency into your code.

seth
  • 36,759
  • 7
  • 60
  • 57
10

I see the subList answer is already here, but here's code that demonstrates that it's a true sublist, not a copy:

public class SubListTest extends TestCase {
    public void testSubarray() throws Exception {
        Integer[] array = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(array);
        List<Integer> subList = list.subList(2, 4);
        assertEquals(2, subList.size());
        assertEquals((Integer) 3, subList.get(0));
        list.set(2, 7);
        assertEquals((Integer) 7, subList.get(0));
    }
}

I don't believe there's a good way to do this directly with arrays, however.

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
9
List.subList(int startIndex, int endIndex)
Joren
  • 3,068
  • 25
  • 44
Manuel Selva
  • 18,554
  • 22
  • 89
  • 134
8

The Lists allow you to use and work with subList of something transparently. Primitive arrays would require you to keep track of some kind of offset - limit. ByteBuffers have similar options as I heard.

Edit: If you are in charge of the useful method, you could just define it with bounds (as done in many array related methods in java itself:

doUseful(byte[] arr, int start, int len) {
    // implementation here
}
doUseful(byte[] arr) {
    doUseful(arr, 0, arr.length);
}

It's not clear, however, if you work on the array elements themselves, e.g. you compute something and write back the result?

akarnokd
  • 69,132
  • 14
  • 157
  • 192
7

One option would be to pass the whole array and the start and end indices, and iterate between those instead of iterating over the whole array passed.

void method1(byte[] array) {
    method2(array,4,5);
}
void method2(byte[] smallarray,int start,int end) {
    for ( int i = start; i <= end; i++ ) {
        ....
    }
}
Sam DeFabbia-Kane
  • 2,599
  • 17
  • 11
6

Java references always point to an object. The object has a header that amongst other things identifies the concrete type (so casts can fail with ClassCastException). For arrays, the start of the object also includes the length, the data then follows immediately after in memory (technically an implementation is free to do what it pleases, but it would be daft to do anything else). So, you can;t have a reference that points somewhere into an array.

In C pointers point anywhere and to anything, and you can point to the middle of an array. But you can't safely cast or find out how long the array is. In D the pointer contains an offset into the memory block and length (or equivalently a pointer to the end, I can't remember what the implementation actually does). This allows D to slice arrays. In C++ you would have two iterators pointing to the start and end, but C++ is a bit odd like that.

So getting back to Java, no you can't. As mentioned, NIO ByteBuffer allows you to wrap an array and then slice it, but gives an awkward interface. You can of course copy, which is probably very much faster than you would think. You could introduce your own String-like abstraction that allows you to slice an array (the current Sun implementation of String has a char[] reference plus a start offset and length, higher performance implementation just have the char[]). byte[] is low level, but any class-based abstraction you put on that is going to make an awful mess of the syntax, until JDK7 (perhaps).

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • Thanks for explaining why it would be impossible. Btw, String now copies on `substring` in HotSpot (forget which build changed this). Why do you say that JDK7 would allow better syntax than ByteBuffer? – Aleksandr Dubinsky Dec 04 '13 at 14:57
  • @AleksandrDubinsky At the time of writing it looked like Java SE 7 was going to allow the array `[]` notation on user-defined types, such as `List` and `ByteBuffer`. Still waiting... – Tom Hawtin - tackline Dec 04 '13 at 23:15
2

@unique72 answer as a simple function or line, you may need to replace Object, with the respective class type you wish to 'slice'. Two variants are given to suit various needs.

/// Extract out array from starting position onwards
public static Object[] sliceArray( Object[] inArr, int startPos ) {
    return Arrays.asList(inArr).subList(startPos, inArr.length).toArray();
}

/// Extract out array from starting position to ending position
public static Object[] sliceArray( Object[] inArr, int startPos, int endPos ) {
    return Arrays.asList(inArr).subList(startPos, endPos).toArray();
}
PicoCreator
  • 9,886
  • 7
  • 43
  • 64
1

I needed to iterate through the end of an array and didn't want to copy the array. My approach was to make an Iterable over the array.

public static Iterable<String> sliceArray(final String[] array, 
                                          final int start) {
  return new Iterable<String>() {
    String[] values = array;
    int posn = start;

    @Override
    public Iterator<String> iterator() {
      return new Iterator<String>() {
        @Override
        public boolean hasNext() {
          return posn < values.length;
        }

        @Override
        public String next() {
          return values[posn++];
        }

        @Override
        public void remove() {
          throw new UnsupportedOperationException("No remove");
        }
      };
    }
  };
}
Owen O'Malley
  • 584
  • 5
  • 7
1

How about a thin List wrapper?

List<Byte> getSubArrayList(byte[] array, int offset, int size) {
   return new AbstractList<Byte>() {
      Byte get(int index) {
         if (index < 0 || index >= size) 
           throw new IndexOutOfBoundsException();
         return array[offset+index];
      }
      int size() {
         return size;
      }
   };
}

(Untested)

RoToRa
  • 37,635
  • 12
  • 69
  • 105
-1

This is a little more lightweight than Arrays.copyOfRange - no range or negative

public static final byte[] copy(byte[] data, int pos, int length )
{
    byte[] transplant = new byte[length];

    System.arraycopy(data, pos, transplant, 0, length);

    return transplant;
}
bond
  • 1,054
  • 8
  • 15