Возьмите сегмент массива в Java без создания нового массива в куче
Я ищу метод на 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 - длина, например.
Ответы
Ответ 1
Отказ от ответственности: этот ответ не соответствует ограничениям вопроса:
Я не хочу создавать новый массив байтов в памяти кучи только для этого.
(Честно говоря, я считаю, что мой ответ достоин удаления. Ответ @unique72 верен. Imma разрешил этому редактированию сидеть немного, а затем я удалю этот ответ.)
Я не знаю, как это сделать непосредственно с массивами без дополнительного распределения кучи, но другие ответы, использующие оболочку поддиректа, имеют дополнительное распределение только для обертки - но не массив - что было бы полезно в случай большого массива.
Тем не менее, если вы ищете краткость, в 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);
Ответ 2
Arrays.asList(myArray)
делегирует новый ArrayList(myArray)
, который не копирует массив, а просто сохраняет ссылку. Используя List.subList(start, end)
после этого делает SubList
, который просто ссылается на исходный список (который все еще просто ссылается на массив). Никакое копирование массива или его содержимого, просто создание оболочки и все связанные списки не поддерживаются исходным массивом. (Я думал, что это будет тяжелее.)
Ответ 3
Если вы ищете подход с псевдонимом стиля указателя, так что вам даже не нужно выделять пространство и копировать данные, тогда я считаю, что вам не повезло.
System.arraycopy()
будет копироваться из вашего источника в пункт назначения, и для этой утилиты испрашивается эффективность. Вам нужно выделить целевой массив.
Ответ 4
Используйте java.nio.Buffer's. Это легкая обертка для буферов различных примитивных типов и помогает управлять нарезкой, позицией, преобразованием, упорядочением байтов и т.д.
Если ваши байты исходят из потока, буферы NIO могут использовать "прямой режим", который создает буфер, поддерживаемый собственными ресурсами. Это может улучшить производительность во многих случаях.
Ответ 5
Один из способов заключается в том, чтобы обернуть массив в 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 может быть немного сложнее понять, но, скорее всего, эффективно реализован и хорошо стоит изучить.
Ответ 6
Вы можете использовать ArrayUtils.subarray в apache commons. Не идеальный, но немного более интуитивный, чем System.arraycopy.
Недостатком является то, что он вводит другую зависимость в ваш код.
Ответ 7
Я вижу ответ 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));
}
}
Я не верю, что есть хороший способ сделать это непосредственно с массивами.
Ответ 8
List.subList(int startIndex, int endIndex)
Ответ 9
Один из вариантов - передать весь массив и начальный и конечный индексы, а также перебрать между ними вместо итерации по всему массиву.
void method1(byte[] array) {
method2(array,4,5);
}
void method2(byte[] smallarray,int start,int end) {
for ( int i = start; i <= end; i++ ) {
....
}
}
Ответ 10
List
позволяет вам прозрачно использовать и работать с subList
. Примитивные массивы потребуют от вас отслеживания какого-либо смещения. ByteBuffer
имеют аналогичные варианты, как я слышал.
Edit:
Если вы отвечаете за полезный метод, вы можете просто определить его с помощью границ (как это сделано во многих связанных с массивом методах в самой java:
doUseful(byte[] arr, int start, int len) {
// implementation here
}
doUseful(byte[] arr) {
doUseful(arr, 0, arr.length);
}
Не ясно, однако, если вы работаете над сами элементами массива, например. вы что-то вычисляете и записываете результат?
Ответ 11
Ссылки Java всегда указывают на объект. Объект имеет заголовок, который, среди прочего, идентифицирует конкретный тип (так что сбрасывание может завершиться с ClassCastException
). Для массивов начало объекта также включает длину, данные затем следуют сразу же в памяти (технически реализация может свободно делать то, что ей нравится, но было бы глупо делать что-либо еще). Итак, вы можете: t иметь ссылку, которая указывает где-то в массив.
В C указателях указывайте где угодно и на что угодно, и вы можете указать на середину массива. Но вы не можете безопасно бросить или узнать, как долго массив. В D указатель содержит смещение в блоке памяти и длине (или, что то же самое, указатель на конец, я не могу вспомнить, что на самом деле делает реализация). Это позволяет D массивам срезов. В С++ у вас будет два итератора, указывающих на начало и конец, но С++ немного странно.
Итак, вернемся к Java, вы не можете. Как уже упоминалось, NIO ByteBuffer
позволяет вам обернуть массив, а затем разрезать его, но дает неудобный интерфейс. Вы можете, конечно, скопировать, что, вероятно, намного быстрее, чем вы думаете. Вы можете представить свою собственную абстракцию String
, которая позволяет вам срезать массив (текущая реализация Sun String
имеет ссылку char[]
плюс начальное смещение и длину, более высокая реализация производительности имеет только char[]
)., byte[]
- это низкий уровень, но любая абстракция на основе классов, которую вы наделаете, сделает ужасный беспорядок синтаксиса, пока JDK7 (возможно).
Ответ 12
@unique72 ответьте как простая функция или строка, вам может потребоваться заменить Object, с соответствующим типом класса, который вы хотите "нарезать". Два варианта даны для удовлетворения различных потребностей.
/// 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();
}
Ответ 13
Как насчет тонкой оболочки 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;
}
};
}
(непроверенные)
Ответ 14
Мне нужно было выполнить итерацию в конце массива и не захотеть скопировать массив. Мой подход состоял в том, чтобы сделать Iterable над массивом.
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");
}
};
}
};
}
Ответ 15
Это немного более легкий, чем 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;
}
Ответ 16
Пожалуйста, используйте
System.arrayCopy();
Он позволяет указать начальную позицию в исходном массиве и количество элементов, которые вы хотите скопировать.