在 Java 中抓取数组的一部分,而无需在堆上创建新数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1100371/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:45:28  来源:igfitidea点击:

Grab a segment of an array in Java without creating a new array on heap

javaarraysslice

提问by jbu

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:

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

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.

例如,我想知道是否有一种方法可以做到doSomething(bigArray.getSubArray(4, 2))4 是偏移量,2 是长度。

回答by Manuel Selva

List.subList(int startIndex, int endIndex)

回答by akarnokd

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

List小号让你与使用和工作subList的东西透明。原始数组需要您跟踪某种偏移量 - 限制。ByteBuffer我听说有类似的选择。

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:

编辑:如果你负责有用的方法,你可以用边界定义它(就像在java本身的许多与数组相关的方法中所做的那样:

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?

但是,不清楚是否对数组元素本身进行处理,例如计算某些内容并写回结果?

回答by djna

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.

System.arraycopy()将从您的源复制到目标,并且此实用程序声称效率高。您确实需要分配目标数组。

回答by Carl Manaster

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

我看到 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));
    }
}

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

但是,我不认为有什么好方法可以直接使用数组来做到这一点。

回答by seth

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

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

回答by Sam DeFabbia-Kane

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++ ) {
        ....
    }
}

回答by James Schek

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.

使用 java.nio.Buffer 的。它是各种原始类型缓冲区的轻量级包装器,有助于管理切片、位置、转换、字节排序等。

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.

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

回答by Tom Hawtin - tackline

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.

Java 引用总是指向一个对象。该对象有一个标头,其中包括标识具体类型(因此强制转换可能会失败ClassCastException)。对于数组,对象的开头还包括长度,然后数据紧跟在内存之后(从技术上讲,实现可以自由地做它想做的事,但做其他任何事情都是愚蠢的)。因此,您不能拥有指向数组某处的引用。

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.

在 C 中,指针指向任何地方和任何东西,您可以指向数组的中间。但是您无法安全地强制转换或找出数组的长度。在 D 中,指针包含内存块和长度的偏移量(或等效地指向末尾的指针,我不记得实现实际上做了什么)。这允许 D 对数组进行切片。在 C++ 中,您将有两个迭代器指向开始和结束,但 C++ 有点奇怪。

So getting back to Java, no you can't. As mentioned, NIO ByteBufferallows 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 Stringhas 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).

所以回到Java,不,你不能。如前所述,NIOByteBuffer允许您包装一个数组然后对其进行切片,但提供了一个笨拙的界面。您当然可以复制,这可能比您想象的要快得多。您可以引入自己的String类似抽象,它允许您对数组进行切片(当前的 Sun 实现String有一个char[]引用加上一个起始偏移量和长度,更高性能的实现只有char[])。byte[]是低级别的,但是您采用的任何基于类的抽象都会使语法变得一团糟,直到 JDK7(也许)。

回答by David J. Liszewski

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.)

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



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?):

也就是说,如果想要简洁,实用方法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);

回答by unique72

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 SubListwhich 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.)

Arrays.asList(myArray)委托给 new ArrayList(myArray),它不复制数组而只存储引用。List.subList(start, end)之后使用使 aSubList只引用原始列表(它仍然只引用数组)。不复制数组或其内容,只创建包装器,所有涉及的列表都由原始数组支持。(我以为它会更重。)