2013-04-22 16 views
11
ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream(); 
ArrayList<Byte> arrayList = new ArrayList<Byte>(); 
try { 
    while (responseStream.available() > 0) { 
     arrayList.add(responseStream.readByte()); 
    } 
} catch (IOException e) { 
    e.printStackTrace(); 
    return internalServerError(); 
} 
Iterator<Byte> iterator = arrayList.iterator(); 
byte[] bytes = new byte[arrayList.size()]; 
int i = 0; 
while (iterator.hasNext()) { 
    bytes[i++] = iterator.next(); 
} 

Questo codice viene richiamato in ogni pagina della mia app Web. Sembra che funzioni abbastanza velocemente, ma c'è qualcosa che potrebbe rendere questa corsa più veloce?Il modo più efficiente per convertire InputStream in byte []?

Edit - Aggiornata con flusso di uscita di byte

ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream(); 
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
try { 
    int read = responseStream.read(); 
    while (read != -1) { 
     byteArrayOutputStream.write(read); 
     read = responseStream.read(); 
    } 
} catch (IOException e) { 
    e.printStackTrace(); 
    return internalServerError(); 
} 
byte[] bytes = byteArrayOutputStream.toByteArray(); 
return ok(bytes).as(response.getHeader("Content-type")); 

Modifica - codice di prova di riferimento

ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream(); 
long t1 = System.nanoTime(); 

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
try { 
    int read = responseStream.read(); 
    while (read != -1) { 
     byteArrayOutputStream.write(read); 
     read = responseStream.read(); 
    } 
} catch (IOException e) { 
    e.printStackTrace(); 
    return internalServerError(); 
} 
byte[] bytes = byteArrayOutputStream.toByteArray(); 

long t2 = System.nanoTime(); 
System.out.println(t2-t1); 
return ok(bytes).as(response.getHeader("Content-type")); 

Tempo medio di 100 + dopo richiesta - 46873

ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream(); 
long t1 = System.nanoTime(); 

ArrayList<Byte> arrayList = new ArrayList<Byte>(); 
try { 
    while (responseStream.available() > 0) { 
     arrayList.add(responseStream.readByte()); 
    } 
} catch (IOException e) { 
    e.printStackTrace(); 
    return internalServerError(); 
} 
Iterator<Byte> iterator = arrayList.iterator(); 
byte[] bytes = new byte[arrayList.size()]; 
int i = 0; 
while (iterator.hasNext()) { 
    bytes[i++] = iterator.next(); 
} 

long t2 = System.nanoTime(); 
System.out.println(t2-t1); 
return ok(bytes).as(response.getHeader("Content-type")); 

Tempo medio dopo 100 + richiesta - 522848

long t1 = System.nanoTime(); 
byte[] bytes; 
try { 
    bytes = org.apache.commons.io.IOUtils.toByteArray(responseStream); 
} catch (Exception e) { 
    return internalServerError(); 
} 

long t2 = System.nanoTime(); 
System.out.println(t2-t1); 

Tempo medio di 100 + dopo richiesta - 45088

long t1 = System.nanoTime(); 
byte[] bytes; 
try { 
    bytes = sun.misc.IOUtils.readFully(responseStream, -1, true); 
} catch (Exception e) { 
    return internalServerError(); 
} 

long t2 = System.nanoTime(); 
System.out.println(t2 - t1); 

Tempo medio di 100 + dopo richiesta - 20180

+0

Hey Matt, ho letto quel post. Sto cercando la massima efficienza. – sissonb

+0

Se sei così preoccupato, dovresti misurare e confrontare le diverse implementazioni tu stesso. Tuttavia, dal momento che hai detto "sembra che funzioni abbastanza velocemente", questo non significa un collo di bottiglia, se stai cercando di rendere il tuo codice più veloce. –

+0

Dai un'occhiata a questo http://stackoverflow.com/questions/6649100/cast-wrapper-array-to-corrispondente-primitive-array –

risposta

12

Sì. Utilizzare uno ByteArrayOutputStream anziché un ArrayList. Quindi leggi i blocchi di byte da InputStream (senza utilizzare available(), che non dovrebbe quasi mai essere utilizzato) e scrivi questi blocchi su ByteArrayOutputStream, finché il metodo read() restituisce -1. Quindi chiama aByteArray() sul tuo ByteArrayOutputStream.

È possibile utilizzare il metodo ByteStreams.toByteArray() di Guava, che fa tutto ciò che è per te, o si può leggere il suo codice sorgente per avere un'idea migliore di come lo fa. La lettura del IO tutorial potrebbe anche aiutare.

+0

Grazie, fammelo testare. Effettuerò anche alcuni test di benchmark – sissonb

+0

'available()' restituisce il numero di byte che possono essere letti senza bloccare. Potrebbe sempre restituire 0 (e lo fa per impostazione predefinita). –

+0

@JBNizet ringraziamenti. – Supericy

1

Perché? Questo codice è interamente equivalente a read(byte[]) con la differenza che esegue due passaggi di copia aggiuntivi sull'intero dato. Non hai bisogno di questo. Un semplice read(byte[]) sarebbe più volte più veloce.

L'uso di available() non è valido. Hai bisogno dell'intera risposta, non solo della parte che può essere letta senza bloccare. Hai bisogno di loop.

+0

Grazie, sto facendo queste correzioni ora. Mostrerò il diff in pochi minuti. – sissonb

+0

Una semplice lettura() non garantisce (a meno che questa implementazione di ChannelBufferInputStream non dia questa garanzia) che l'intero flusso sia letto. Forse non è quello che volevi dire, ma hai bisogno di un ciclo di lettura fino a quando non viene restituito -1. –

+0

@JBNizet Concordato, chiarito. – EJP

4

Cosa c'è che non va nel metodo Apache Commons IO IOUtils.toByteArray? Questo è stato ottimizzato per molti anni per questo scopo.

+3

Ehi, non volevo importare una libreria per una sola funzione. – sissonb

+1

Bene, è probabile che utilizzerai più funzioni in quella libreria nel tempo e qual è il problema? Ad ogni modo, è open source. Leggi la fonte e guarda come lo fanno se non vuoi il tutto. – bmargulies

+0

E riguardo l'uso di 'sun.misc.IOUtils'? È integrato in Java, ma ho sentito di non fidarsi delle librerie di Sun. 'IOUtils.readFully (responseStream, -1, true);' Questo mi sta dando risultati più veloci però. Forse mi arrenderò e userò i comuni Apache ... – sissonb

Problemi correlati