我正在寻找一种Java方法,它将返回一个数组的一部分。一个例子是获取包含字节数组的第4和第5个字节的字节数组。我不想在堆内存中创建一个新的字节数组来做到这一点。现在我有以下代码:
doSomethingWithTwoBytes(byte[] twoByteArray);
void someMethod(byte[] bigArray)
{
byte[] x = {bigArray[4], bigArray[5]};
doSomethingWithTwoBytes(x);
}
我想知道是否有办法只做doSomething(bigArray.getSubArray(4, 2))
,其中4是偏移量,2是长度,例如。
答案 0 :(得分:183)
我不想在堆内存中创建一个新的字节数组来执行此操作。
(老实说,我觉得我的答案值得删除。@ unique72的答案是正确的.Imma让这个编辑稍等一下然后我会删除这个答案。)
我不知道如何直接对没有额外堆分配的数组执行此操作,但使用子列表包装器的其他答案仅对包装器进行额外分配 - 但不包括数组 - 这对于大阵列的情况。
那就是说,如果一个人正在寻求简洁,那么实用工具方法Arrays.copyOfRange()
是在Java 6(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);
答案 1 :(得分:165)
Arrays.asList(myArray)
委托给新的ArrayList(myArray)
,它不复制数组但只存储引用。之后使用List.subList(start, end)
创建一个SubList
,它只引用原始列表(它仍然只引用数组)。不复制数组或其内容,只创建包装器,所有涉及的列表都由原始数组支持。 (我以为它会更重。)
答案 2 :(得分:39)
如果您正在寻找指针式样式别名方法,那么您甚至不需要分配空间并复制数据,那么我相信您运气不佳。
System.arraycopy()
将从您的来源复制到目的地,并声明此实用程序的效率。您需要分配目标数组。
答案 3 :(得分:21)
一种方法是将数组包装在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
理解起来可能有点棘手,但很可能有效实施,非常值得学习。
答案 4 :(得分:20)
使用java.nio.Buffer。它是各种原始类型缓冲区的轻量级包装器,有助于管理切片,位置,转换,字节排序等。
如果您的字节来自Stream,则NIO缓冲区可以使用“直接模式”,这将创建由本机资源支持的缓冲区。这可以在很多情况下提高性能。
答案 5 :(得分:14)
您可以在apache commons中使用ArrayUtils.subarray。不完美但比System.arraycopy.
更直观。缺点是它确实在你的代码中引入了另一个依赖。
答案 6 :(得分:10)
我看到subList的答案已经在这里,但是这里的代码证明它是一个真正的子列表,而不是副本:
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));
}
}
我不相信有一种直接用数组做这个的好方法。
答案 7 :(得分:9)
List.subList(int startIndex, int endIndex)
答案 8 :(得分:7)
一个选项是传递整个数组以及开始和结束索引,并在这些索引之间进行迭代,而不是遍历传递的整个数组。
void method1(byte[] array) {
method2(array,4,5);
}
void method2(byte[] smallarray,int start,int end) {
for ( int i = start; i <= end; i++ ) {
....
}
}
答案 9 :(得分:6)
List
允许您透明地使用和使用subList
内容。原始数组需要您跟踪某种偏移 - 限制。 ByteBuffer
有类似我听到的选项。
修改强> 如果你负责有用的方法,你可以用bounds定义它(就像在java本身的许多数组相关的方法中所做的那样:
doUseful(byte[] arr, int start, int len) {
// implementation here
}
doUseful(byte[] arr) {
doUseful(arr, 0, arr.length);
}
但是,如果您自己处理数组元素,则不清楚,例如你计算一些东西并回写结果?
答案 10 :(得分:6)
Java引用始终指向一个对象。该对象有一个标题,其中包括标识具体类型(因此强制转换可能会失败ClassCastException
)。对于数组,对象的开始还包括长度,然后数据紧跟在内存中(技术上,一个实现可以自由地做它喜欢的事情,但是做其他任何事情都是愚蠢的)。所以,你可以有一个指向某个数组的引用。
在C指针指向任何地方和任何地方,你可以指向数组的中间。但你不能安全地投射或找出阵列有多长。在D中,指针包含内存块和长度的偏移量(或者等效于指向结尾的指针,我不记得实现实际上做了什么)。这允许D切片数组。在C ++中,你会有两个指向开头和结尾的迭代器,但是C ++有点奇怪。
所以回到Java,不,你不能。如上所述,NIO ByteBuffer
允许您包装数组然后对其进行切片,但是会给出一个尴尬的界面。你当然可以复制,这可能比你想象的要快得多。您可以引入自己的String
- 类似抽象,允许您对数组进行切片(String
的当前Sun实现具有char[]
引用加上起始偏移和长度,更高性能实现拥有char[]
)。 byte[]
是低级别的,但是你提出的任何基于类的抽象都会使语法变得非常混乱,直到JDK7(可能)。
答案 11 :(得分:2)
@ unique72回答作为一个简单的函数或行,你可能需要用你想要的相应类类型替换对象&#39; slice&#39;。提供两种变体以满足各种需求。
/// 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();
}
答案 12 :(得分:1)
瘦List
包装器怎么样?
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;
}
};
}
(未测试的)
答案 13 :(得分:1)
我需要遍历数组的末尾,并且不想复制数组。我的方法是在数组上进行Iterable。
{{1}}
答案 14 :(得分:-1)
这比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;
}