在Java中抓取数组的一部分,而不用在堆上创build新的数组

我正在寻找一种Java方法,将返回一个数组的一部分。 一个例子是获取包含字节数组的第4个和第5个字节的字节数组。 我不想在堆内存中创build一个新的字节数组,只是为了做到这一点。 现在我有以下代码:

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

我想知道是否有办法做doSomething(bigArray.getSubArray(4, 2)) ,其中4是偏移,2是长度,例如。

免责声明:这个答案不符合这个问题的限制:

我不想在堆内存中创build一个新的字节数组,只是为了做到这一点。

老实说,我觉得我的答案值得删除,@ unique72的答案是正确的,Imma让这个编辑坐了一会儿,然后我会删除这个答案。


我不知道有一种方法直接与数组没有额外的堆分配,但使用子列表包装的其他答案只有包装的额外分配 – 而不是数组 – 这将是有用的情况下一个大阵。

也就是说,如果一个人在寻求简洁性,那么实用的方法Arrays.copyOfRange()是在Java 6(2006年末Arrays.copyOfRange()中引入的:

 byte [] a = new byte [] {0, 1, 2, 3, 4, 5, 6, 7}; // get a[4], a[5] byte [] subArray = Arrays.copyOfRange(a, 4, 6); 

Arrays.asList(myArray)委托给新的ArrayList(myArray) ,它不复制数组,但只存储引用。 之后使用List.subList(start, end)使得一个只引用原始列表的子列表(它仍然引用数组)。 不复制数组或其内容,仅创build包装器,以及涉及的所有列表均由原始数组支持。 (我认为这会更重。)

如果你正在寻找一种指针式的别名方法,那么你甚至不需要分配空间和复制数据,那么我相信你是运气不好的。

System.arraycopy()将从您的源复制到目的地,效率要求为此实用程序。 您确实需要分配目标数组。

使用java.nio.Buffer的。 它是各种基本types的缓冲区的轻量级包装,并帮助pipe理切片,位置,转换,字节sorting等。

如果您的字节来源于Stream,则NIO缓冲区可以使用“直接模式”,从而创build由本地资源支持的缓冲区。 这可以在很多情况下提高性能。

一种方法是将数组封装在java.nio.ByteBuffer中,使用绝对的put / get函数,并将缓冲区分片以在子数组上工作。

例如:

 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()); } 

注意你必须调用wrap()和slice(),因为wrap()本身只影响相对的put / get函数,而不是绝对的。

ByteBuffer可能有点难以理解,但最有可能有效的实现,值得学习。

你可以在apache的commons中使用ArrayUtils.subarray 。 不完美,但比System.arraycopy.更直观System.arraycopy. 缺点是它确实在你的代码中引入了另一个依赖。

我看到子列表的答案已经在这里,但是这里的代码表明它是一个真正的子列表,而不是副本:

 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)); } } 

但是我不相信有一个好的方法可以直接用数组来完成。

 List.subList(int startIndex, int endIndex) 

一个select是传递整个数组和开始和结束索引,并在这些之间进行迭代,而不是遍历整个传递的数组。

 void method1(byte[] array) { method2(array,4,5); } void method2(byte[] smallarray,int start,int end) { for ( int i = start; i <= end; i++ ) { .... } } 

List允许您透明地使用和处理子List 。 原始数组将要求您跟踪某种偏移限制。 据我ByteBuffer有类似的选项。

编辑:如果你负责的有用的方法,你可以只是定义它的界限(如在java本身的许多数组相关的方法:

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

但是,如果你自己处理数组元素,例如计算一些东西并写回结果,那还不清楚。

Java引用总是指向一个对象。 该对象有一个标题,其中标识了具体的types(所以强制types转换ClassCastException可能会失败)。 对于数组来说,对象的开始还包括长度,然后在内存中紧跟着数据(从技术上来说,一个实现可以自由地执行任意操作,但是执行其他任何操作都是愚蠢的)。 所以,你可以有一个引用,指向一个数组的某个地方。

在C指针指向任何地方,任何东西,你可以指向一个数组的中间。 但是你不能安全地投射或找出arrays有多长。 在D中,指针包含一个到内存块和长度的偏移(或者等价地指向结尾的指针,我不记得实现的实际内容)。 这允许D切片数组。 在C ++中,你会有两个指向开始和结束的迭代器,但是C ++有点奇怪。

所以回到Java,不,你不能。 如前所述,NIO ByteBuffer允许你包装一个数组,然后对其进行分片,但给出一个尴尬的界面。 你当然可以复制,这可能比你想象的要快得多。 你可以引入你自己的String类抽象,允许你切片数组(当前Sun的String实现有一个char[]引用加上一个起始偏移量和长度,性能较高的实现只需要char[] )。 byte[]是低级别的,但是在JDK7之前(或许),任何基于类的抽象概念都会使语法变得糟糕。

@ unique72答案作为一个简单的函数或行,您可能需要replace对象,与相应的类types,你希望“切片”。 给出了两种变体以适应各种需求。

 /// 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(); } 

如何瘦薄膜包装?

 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; } }; } 

(未testing)

我需要遍历数组的结尾,并不想复制数组。 我的方法是对数组进行迭代。

 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"); } }; } }; } 

这比Arrays.copyOfRange轻一点 – 没有范围或负面的

 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; } 

请用

 System.arrayCopy(); 

它允许您指定源数组中的起始位置和要复制的元素数量。