A classe ByteArrayInputStream no pacote java.io pode ser usada para ler uma matriz de entrada (de bytes).

Para criar um fluxo de entrada de matriz de bytes, devemos primeiro importar o pacote java.io.ByteArrayInputStream . Depois de importar o pacote, temos dois construtores disponíveis para criar um fluxo de entrada:


ByteArrayInputStream input = new ByteArrayInputStream(arr);
ByteArrayInputStream input = new ByteArrayInputStream(arr, 2, 2);
    

Existem 4 campos dentro da classe:


// Byte array provided by the creator of the stream
protected byte buf[];

// Index of the next character to read from the input stream's buffer
protected int pos;

// Current marked position in the stream
protected int mark = 0;

// Index is one greater than the last valid character in the input stream's buffer
protected int count;
    

E aqui estão nossos construtores:


public ByteArrayInputStream(byte buf[]) {
    this.buf = buf;
    this.pos = 0;
    this.count = buf.length;
}

public ByteArrayInputStream(byte buf[], int offset, int length) {
    this.buf = buf;
    this.pos = offset;
    this.count = Math.min(offset + length, buf.length);
    this.mark = offset;
}
    

Métodos da classe ByteArrayInputStream

Método Ação
int ler() Lê o próximo byte de dados deste fluxo de entrada.
int read(byte b[], int off, int len) Lê vários bytes do fluxo de entrada e os armazena na matriz de buffer b .
off é um deslocamento na matriz de destino b .
len é o número máximo de bytes a serem lidos.
salto longo (n longo) Ignora n bytes de entrada deste fluxo de entrada. Retorna o número de bytes ignorados (pode ser menor que n se chegarmos ao final do fluxo de entrada).
int disponível() Retorna o número de bytes restantes que podem ser lidos (ou ignorados) desse fluxo de entrada.
void redefinir() Redefine o buffer para a posição marcada. A posição marcada é 0, a menos que outra posição seja marcada ou um deslocamento diferente seja especificado no construtor.
marca booleanaSupported() Verifica se este InputStream suporta marcação/redefinição. Retorna true para ByteArrayInputStream .
void fechar() Não faz nada.
void mark(int readAheadLimit) define omarcacampo igual à posição atual. Se o método de reinicialização for chamado, a leitura subsequente começará a partir dessa posição. O parâmetro readAheadLimit não é usado e não afeta o comportamento do método.

Vamos dar uma olhada nesses métodos e ver como eles funcionam na prática.

ler()

Quando você quiser ler bytes de um ByteArrayInputStream como faria em um InputStream comum , você pode usar o método read() .


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       for (int i = 0; i < array.length; i++) {
           int data = input.read();
           System.out.print(data + ", ");
       }
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

disponível()

Se você quiser verificar se há algo em seu buffer, você pode chamar o método available() .


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       System.out.println("Bytes available for reading: " + input.available());

       input.read();
       System.out.println("Bytes available for reading " + input.available());

       input.read();
       System.out.println("Bytes available for reading " + input.available());
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

Veremos que o número de bytes disponíveis para leitura muda após cada leitura do buffer.

Saída:

Bytes disponíveis para leitura: 4
Bytes disponíveis para leitura: 3
Bytes disponíveis para leitura: 2

pular (longo n)

Você pode usar o método skip() para pular um certo número de bytes e não lê-los.


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       input.skip(2);

       while (input.available() != 0) {
           int data = input.read();
           System.out.print(data + ", ");
       }
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

Saída:

3, 4,

reiniciar()

Este método redefine a posição do fluxo em buffer para a última posição marcada. É a posição 0, a menos que uma marca diferente seja definida.


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream input = new ByteArrayInputStream(buf)) {
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());

       System.out.println("Calling reset() method");
       input.reset();
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

Veremos que chamar o método reset() nos leva ao ponto inicial do nosso stream.

Saída:

Leitura: 65
Leitura: 66
Leitura: 67
Leitura: 68
Chamando o método reset()
Leitura: 65
Leitura: 66

mark(int readAheadLimit)

O método mark() da classe ByteArrayInputStream define a marca interna na posição atual do byte, ou seja, imediatamente após o byte lido anteriormente. Este método recebe um parâmetro que indica quantos bytes podem ser lidos após a marca antes que seu fluxo se torne inválido. Por padrão, se a marca não for definida explicitamente, um ByteArrayInputStream marca a posição 0 ou a posição do deslocamento passado para seu construtor. É importante observar que a marca readAheadLimit é irrelevante para esta classe.


/* Note: For this class, {@code readAheadLimit}
*  has no meaning.
*
* @since   1.1
*/
public void mark(int readAheadLimit) {
   mark = pos;
}
    

Aqui está um exemplo de como definir uma marca em um ByteArrayInputStream usando seus métodos mark() e reset() . Adicionaremos uma chamada ao método mark() ao exemplo anterior:


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream input = new ByteArrayInputStream(buf)) {
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       input.mark(5);

       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());

       System.out.println("Calling reset() method");
       input.reset();

       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());

   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

Podemos ver que a posição do fluxo atual mudou.

Saída:

Leitura: 65
Leitura: 66
Leitura: 67
Leitura: 68
Leitura: 69
Chamando o método reset()
Leitura: 68
Leitura: 69

markSupported()

O método markSupported() permite verificar se uma marca pode ser definida. Para entender de onde vem o valor de retorno, vamos ao código do método:


/**
* Tests if this {@code InputStream} supports mark/reset. The
* {@code markSupported} method of {@code ByteArrayInputStream}
* always returns {@code true}.
*
* @since   1.1
*/
public boolean markSupported() {
   return true;
}
    

O método sempre retorna true . Vamos testar isso na prática.


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream bais = new ByteArrayInputStream(buf)) {
       boolean isMarkSupported = bais.markSupported();

       System.out.println("isMarkSupported: " + isMarkSupported);
       System.out.println("Read: " + bais.read());
       System.out.println("Read: " + bais.read());

       bais.mark(1);
       System.out.println("Read: " + bais.read());
       isMarkSupported = bais.markSupported();
       System.out.println("isMarkSupported: " + isMarkSupported);

       bais.reset();
       isMarkSupported = bais.markSupported();
       System.out.println("isMarkSupported: " + isMarkSupported);
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

Depois de executar os métodos mark() e reset() , nosso stream está sempre pronto e suporta marcas:

Saída:

isMarkSupported: verdadeiro
Leitura: 65
Leitura: 66
Leitura: 67
isMarkSupported: verdadeiro
isMarkSupported: verdadeiro

fechar()

E para entender o método close , vamos também dar uma espiada nele:


/**
* Closing a {@code ByteArrayInputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*/
public void close() throws IOException {
}
    

A documentação do método close informa que fechar um ByteArrayInputStream não tem efeito. Os métodos da classe ByteArrayInputStream podem ser chamados após o fluxo ser fechado sem gerar uma IOException .

O que podemos concluir?

Precisamos de um ByteArrayInputStream quando queremos ler dados de uma matriz de bytes. Geralmente faz sentido usar essa classe em combinação com outro código que saiba como trabalhar com InputStreams em vez de sozinho.