2015-09-30 4 views
2

Учитывая объект byte[], когда мы хотим работать с таким объектом, нам часто нужны кусочки. В моем конкретном примере я получаю byte[] от проводов, где первые 4 байта описывают длину сообщения, а затем еще 4 байта типа сообщения (целое число, которое сопоставляется конкретному классу protobuf), а затем оставшееся byte[] - это фактическое содержимое сообщения ... например этоКак передать часть массива?

length|type|content 

для того, чтобы разобрать это сообщение я должен передать содержательную часть к определенному классу, который знает, как разобрать экземпляр из нее ... проблема в том, что часто не существует никаких методов, предоставляемые таким образом, чтобы вы могли указать от где до где анализатор должен прочитать массив ...

Итак, что мы делаем, это копирование оставшихся пакетов этого массива, что неэффективно ...

Насколько я знаю, в Java не возможно создать другой byte[] ссылку, которая на самом деле относится к некоторым оригинальным большим byte[] массив с помощью всего 2 индексов (это был подход с Последовательностью, что привело к утечке памяти) ...

Интересно, как мы решаем такие ситуации? небось отказ от Protobuf только потому, что он не обеспечивает некоторые parseFrom(byte[], int, int) не делает Sence ... Protobuf это просто пример, что-то может не хватает, что апи ...

Так же эта сила нас писать неэффективный код или есть что-то, что можно сделать? (appart from add the method) ...

+2

использовать 'ByteBuffer'; это лучший класс для этого в JDK. Манипулировать это немного сложно (остерегайтесь позиций!). – fge

+0

Я знаю, но даже с bytebuffer у вас нет синтаксиса (ByteBuffer, int, int), когда вы получите оставшееся от bytebuffer, вы создадите еще один байт [], чего я хочу избежать ... – vach

+1

Извините, но Я не понимаю вашу точку зрения. Если вам действительно нужен массив, который является только частью оригинала, у вас действительно нет выбора. – fge

ответ

1

В Java Array - это не просто раздел памяти - это объект, у которого есть дополнительные поля (по крайней мере - длина). Таким образом, вы не можете ссылаться на часть массива - вы должны:

  • Использование функций массива копирование или
  • Реализовать и использовать некоторый алгоритм, который использует только часть массива байтов.
1

Концерн кажется, что нет способа создать представление по массиву (например, эквивалент массива List#subList()). Обходной путь может привести к тому, что ваши методы синтаксического анализа возьмут ссылку на весь массив и два индекса (или индекс и длину), чтобы указать вспомогательный массив, над которым должен работать метод.

Это не помешает методам чтения или изменения разделов массива, к которым они не должны касаться. Возможно, ByteArrayView класс может быть сделано, чтобы добавить немного безопасности, если это является проблемой:

public class ByteArrayView { 
    private final byte[] array; 
    private final int start; 
    private final int length; 

    public ByteArrayView(byte[] array, int start, int length) { ... } 

    public byte[] get(int index) { 
    if (index < 0 || index >= length) { 
     throw new ArrayOutOfBoundsExceptionOrSomeOtherRelevantException(); 
    } 
    return array[start + index]; 
    } 
} 

Но если, с другой стороны, производительность является проблемой, то метод вызова get() для выборки каждого байта вероятно, нежелательно.

Код для иллюстрации; это не проверено или что-то еще.

EDIT

На втором чтении моего ответа, я понял, что я должен указать на это: наличие ByteArrayView будет копировать каждые байты, считываемые из исходного массива - только побайтно, а не как кусок. Это было бы неадекватно для проблем ОП.

2

Обычно вы должны заниматься такими вещами с потоками.

Поток - это абстракция для чтения только того, что вам нужно для обработки текущего блока данных. Таким образом, вы можете прочитать правильное количество байтов в массив байтов и передать его в свою функцию синтаксического анализа.

Вы спрашиваете: «И это заставляет нас писать неэффективный код, или есть что-то, что можно сделать?»

Обычно вы получаете свои данные в виде потока, а затем, используя технику, продемонстрированную ниже, будет более результативной, потому что вы пропустите создание одной копии. (Две копии вместо трех, один раз ОС и один раз вами. Вы пропустите копию полного массива байтов, прежде чем начинать синтаксический анализ.) Если вы действительно начинаете с byte[], но он построен сам, тогда вы можете захотеть вместо этого нужно изменить конструкцию объекта, например { int length, int type, byte[] contentBytes }, и передать contentBytes в вашу функцию синтаксического анализа.

Если вам действительно нужно начинать с byte[], тогда техника ниже - это более удобный способ ее разобрать, это не будет более результативным.

Итак, предположим, что у вас есть буфер байтов откуда-то, и вы хотите прочитать содержимое этого буфера. Сначала вы преобразовать его в поток:

private static List<Content> read(byte[] buffer) { 
    try { 
     ByteArrayInputStream bytesStream = new ByteArrayInputStream(buffer); 
     return read(bytesStream); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

выше функция оборачивает массив с потоком и передает его в функцию, которая делает фактическое чтение. Если вы можете начать из потока, то, очевидно, вы можете пропустить предыдущий шаг и просто передать этот поток в следующей функции непосредственно:

private static List<Content> read(InputStream bytesStream) throws IOException { 
    List<Content> results = new ArrayList<Content>(); 
    try { 
     // read the content... 
     Content content1 = readContent(bytesStream); 
     results.add(content1); 

     // I don't know if there's more than one content block but assuming 
     // that there is, you can just continue reading the stream... 
     // 
     // If it's a fixed number of content blocks then just read them one 
     // after the other... Otherwise make this a loop 
     Content content2 = readContent(bytesStream); 
     results.add(content2); 
    } finally { 
     bytesStream.close(); 
    } 
    return results; 
} 

Поскольку ваш байт-массив содержит контент, вы хотите, чтобы прочитать содержимое блоков из поток. Поскольку у вас есть длина и тип поля, я предполагаю, что у вас есть разные блоки контента. Следующая функция считывает длину и тип и проходит обработку байт контента на соответствующую классу в зависимости от типа чтения:

private static Content readContent(InputStream stream) throws IOException { 
    final int CONTENT_TYPE_A = 10; 
    final int CONTENT_TYPE_B = 11; 

    // wrap the InputStream in a DataInputStream because the latter has 
    // convenience functions to convert bytes to integers, etc. 
    // Note that DataInputStream handles the stream in a BigEndian way, 
    // so check that your bytes are in the same byte order. If not you'll 
    // have to find another stream reader that can convert to ints from 
    // LittleEndian byte order. 
    DataInputStream data = new DataInputStream(stream); 
    int length = data.readInt(); 
    int type = data.readInt(); 

    // I'm assuming that above length field was the number of bytes for the 
    // content. So, read length number of bytes into a buffer and pass that 
    // to your `parseFrom(byte[])` function 
    byte[] contentBytes = new byte[length]; 
    int readCount = data.read(contentBytes, 0, contentBytes.length); 
    if (readCount < contentBytes.length) 
     throw new IOException("Unexpected end of stream"); 

    switch (type) { 
     case CONTENT_TYPE_A: 
      return ContentTypeA.parseFrom(contentBytes); 
     case CONTENT_TYPE_B: 
      return ContentTypeB.parseFrom(contentBytes); 
     default: 
      throw new UnsupportedOperationException(); 
    } 
} 

я составил следующие классы содержания. Я не знаю, что protobuf есть, но это, по-видимому преобразовать из массива байтов до фактического объекта с его parseFrom(byte[]) функции, так что принять это как псевдокод:

class Content { 
    // common functionality 
} 

class ContentTypeA extends Content { 
    public static ContentTypeA parseFrom(byte[] contentBytes) { 
     return null; // do the actual parsing of a type A content 
    } 
} 

class ContentTypeB extends Content { 
    public static ContentTypeB parseFrom(byte[] contentBytes) { 
     return null; // do the actual parsing of a type B content 
    } 
} 
Смежные вопросы