52

I have some library of classes, working with my data, which is being read into buffer. Is it possible somehow to avoid copying arrays again and again, passing parts of data deeper and deeper into processing methods? Well, it sounds strange, but in my particular case, there's a special writer, which divides data into blocks and writes them individually into different locations, so it just performs System.arraycopy, gets what it needs and calls underlying writer, with that new sub array. And this happens many times. What is the best approach to refactor such code?

Roman Nikitchenko
  • 12,800
  • 7
  • 74
  • 110
Illarion Kovalchuk
  • 5,774
  • 8
  • 42
  • 54

9 Answers9

68
Arrays.asList(array).subList(x, y).

This method doesn't give you an array, but a List, which is far more flexible.

rootmeanclaire
  • 808
  • 3
  • 13
  • 37
Ricky Clarkson
  • 2,909
  • 1
  • 19
  • 21
24

Many classes in Java accept a subset of an arrays as parameter. E.g. Writer.write(char cbuf[], int off, int len). Maybe this already suffices for your usecase.

Markus Kull
  • 1,471
  • 13
  • 16
14

There is no real way to wrap any data without copying and receive real array in Java. You just cannot create new array over existing memory. You have basically 2 options:

  • Use methods that can accept range of array. This was already recommended.
  • Use wrapper that gives some kind of abstraction that is close to array and is suitable for many applications. Will be described below.

You may use java.nio.Buffer classes hierarchy, especially java.nio.ByteBuffer which offers buffer abstraction on whole array or sub-ranges. Often it is what people need. This also offers many interesting abilities like 'zero copy' flip and flexible byte area representation.

Here is example of wrapping using java.nio.ByteBuffer. This should be very close to what you need. At least for some operations.

byte [] a1 = {0, 0, 1, 0};
ByteBuffer buf = ByteBuffer.wrap(a1,1,2);

Then you can do on buf any ByteBuffer operation.

Just a warning, buf.array() returns original a1 array (backend) with all elements.

Roman Nikitchenko
  • 12,800
  • 7
  • 74
  • 110
  • 1
    Ah so even if I do buf = ByteBuffer.wrap(a1, 1, 2)... buf.array() will still return {0, 0, 1, 0}. So this idea can't really be used to get a subarray? – Burrito Dec 23 '17 at 21:56
  • You just cannot get real sub-array without copying in Java. So wrappers are used. List one was illustrated before and Buffer abstraction is another one. I would say it is far more useful over byte range of memory but for complex object arrays List is more common. – Roman Nikitchenko Dec 25 '17 at 12:47
  • 1
    This is a knowledgable/in depth answer – WestCoastProjects Apr 27 '18 at 04:19
4

There is no way to declare a subarray in Java if you use built in arrays like byte[]. The reason is: The length of the array is stored with the data, not with the declaration of the reference to it. Hence a subarray which does not copy the data has no place where it can store the length! So for basic types you can use the mentioned efficient byte array copies and for higher types (List) there are methods available.

Joachim
  • 41
  • 1
2

You could take the same approach as the String class takes; create a class for immutable objects which are constructed from an array, a start offset and an end offset which offers access to the sub-array. The user of such an object does not have to know the distinction between the whole array or a sub-array. The constructor does not have to copy the array, just store the array reference and its boundaries.

rsp
  • 23,135
  • 6
  • 55
  • 69
1

Perhaps instead of working with arrays you should work with a different type that maintains a reference to a slice of the original array, instead of copying the data over, similar to ArraySegment in C#. An additional benefit to this is that you can also shift the slice over the original array on-demand, without creating new instances. Pseudo code:

public class ArraySegment<T> implements Iterable<T> 
{
      private int from, to;
      private T[] original;
      
      public ArraySegment<T>(T[] original, int from, int to)
      {
          //constructor stuff
      }

      public T get(int index)
      {
           return original[index + from];
      }

      public int size()
      {
          return to - from + 1;
      }
      
      @Override
      public Iterator<T> iterator()
      {
          //Iterator that iterates over the slice
      }

      //Can support setters on from/to variables
}
Orestis P.
  • 805
  • 7
  • 27
1

You could use (ArrayList).subList(value1, value2) i belive, perhaps that could help in your case? That is ofcourse if you want to use an ArrayList.

Kotten
  • 21
  • 1
0

Google's Guava libraries support the slice concept in the form of a ByteSource.

Google Guava is a readily available open-source package of functionality, written from the ground up to follow Google best practices, which depend on significant array slicing capabilities.

djenning90
  • 197
  • 1
  • 9
-3

Have a look on Arrays.copyOfRange(***) methods.

Manuel Selva
  • 18,554
  • 22
  • 89
  • 134
  • 15
    From javadoc: "Copies the specified range of the specified array into a new array", which is not what the OP wants. – f1sh Aug 03 '10 at 11:28