2013-10-19 13 views
6

Il titolo dice tutto. C'è un modo per convertire da StringBuilder a byte [] senza usare una stringa nel mezzo?Java: StringBuffer al byte [] senza toString

Il problema è che sto gestendo VERAMENTE grandi stringhe (milioni di caratteri), e quindi ho un ciclo che aggiunge un carattere alla fine e ottiene il byte []. Il processo di conversione di StringBuffer in String rende questo ciclo moltoyyy molto molto lento.

C'è qualche modo per realizzare questo? Grazie in anticipo!

+0

Il più vicino che si può ottenere è ottenere una matrice 'char []'. StringBuffer # getChars (int, int, char [], int) –

+2

perché non utilizzare [CharBuffer] (http://docs.oracle.com/javase/7/docs/api/java/nio/CharBuffer.html)? E poi "charBuffer.array()"? – tolitius

+2

Puoi chiarire perché è necessario memorizzare tutte queste grandi stringhe in memoria? È qualcosa su cui un utente sta aspettando? Potrebbe invece diventare un lavoro MapReduce o Spark? Mi chiedo solo se forse questa domanda è un sintomo di un odore di progettazione architettonica. – Vidya

risposta

1

Per cominciare, si dovrebbe probabilmente essere utilizzando StringBuilder, dal momento che ha in testa StringBuffer sincronizzazione che di solito è inutile.

Purtroppo, non c'è modo di andare direttamente alla byte s, ma è possibile copiare i char s in un array o iterare 0-length() e leggere ogni charAt().

+0

+1 E Javadoc per StringBuffer dice che dovresti usare StringBuilder da quasi dieci anni. –

0

Cosa stai cercando di ottenere con "milioni di caratteri"? Questi log devono essere analizzati? Puoi leggerlo come solo byte e attenersi a uno ByteBuffer? Poi si può fare:

buffer.array() 

per ottenere un byte[]

dipende da ciò che è si sta facendo, è possibile utilizzare anche solo una char[] o un CharBuffer:

CharBuffer cb = CharBuffer.allocate(4242); 
cb.put("Depends on what it is you need to do"); 
... 

Poi si può ottenere un char[] come:

cp.array() 

È sempre bene REPLICARE le cose, è divertente e dimostra il punto. Java REPL non è qualcosa che siamo abituati, ma hey, c'è Clojure per salvare il giorno in cui si parla di Java correntemente:

user=> (import java.nio.CharBuffer) 
java.nio.CharBuffer 

user=> (def cb (CharBuffer/allocate 4242)) 
#'user/cb 

user=> (-> (.put cb "There Be") (.array)) 
#<char[] [[email protected]> 

user=> (-> (.put cb " Dragons") (.array) (String.)) 
"There Be Dragons" 
11

Come molti hanno già suggerito, è possibile utilizzare la classe CharBuffer, ma l'assegnazione di una nuova CharBuffer avrebbe solo peggiorato il tuo problema

Invece, si può avvolgere direttamente il vostro StringBuilder in una CharBuffer, dal momento che StringBuilder implementa CharSequence:

Charset charset = StandardCharsets.UTF_8; 
CharsetEncoder encoder = charset.newEncoder(); 

// No allocation performed, just wraps the StringBuilder. 
CharBuffer buffer = CharBuffer.wrap(stringBuilder); 

ByteBuffer bytes = encoder.encode(buffer); 

EDIT: Duarte fa giustamente notare che il metodo CharsetEncoder.encode può restituire un buffer il cui allineamento supporto è più grande il dato attuale - il significato, la sua capacità è maggiore del suo limite. È necessario leggere il ByteBuffer stesso o leggere un array di byte dal ByteBuffer che ha le dimensioni corrette. In quest'ultimo caso, non c'è nessun evitando avere due copie dei byte in memoria, anche se brevemente:

ByteBuffer byteBuffer = encoder.encode(buffer); 

byte[] array; 
int arrayLen = byteBuffer.limit(); 
if (arrayLen == byteBuffer.capacity()) { 
    array = byteBuffer.array(); 
} else { 
    // This will place two copies of the byte sequence in memory, 
    // until byteBuffer gets garbage-collected (which should happen 
    // pretty quickly once the reference to it is null'd). 

    array = new byte[arrayLen]; 
    byteBuffer.get(array); 
} 

byteBuffer = null; 
+0

+1 per la risposta corretta che implementa anche correttamente la codifica charset. –

+1

Attenzione: ByteBuffer.array() restituisce l'intero array di supporto, che conterrà probabilmente byte extra! –

0

Se si desidera prestazioni, non vorrei usare StringBuilder o creare un byte []. Invece puoi scrivere progressivamente allo stream che prenderà i dati in primo luogo. Se non è possibile farlo, è possibile copiare i dati da StringBuilder a Writer, ma è molto più veloce non creare StringBuilder in primo luogo.

+0

Come procedere per scrivere progressivamente nello stream? Ho una funzione che contiene byte [] – CyberMew

+0

Hai bisogno di una funzione che puoi chiamare con il byte [] che hai letto fino ad ora, ad es. https://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html#write(byte[],%20int,%20int) Questa funzione consente di utilizzare lo stesso byte [] ciascuno tempo così rendendo costante il consumo di memoria e la spazzatura indipendentemente dalle dimensioni dei dati elaborati. –

1

Sfortunatamente, le risposte sopra che trattano il metodo array() di ByteBuffer sono un po 'buggy ... Il problema è che il byte assegnato [] è probabilmente più grande di quello che ci si aspetterebbe. Quindi, ci saranno dei byte NULL finali che sono difficili da eliminare, dal momento che non è possibile "ridimensionare" gli array in Java.

Ecco un articolo che spiega più in dettaglio: http://worldmodscode.wordpress.com/2012/12/14/the-java-bytebuffer-a-crash-course/

2

Se siete disposti a sostituire il StringBuilder con qualcos'altro, ma un'altra possibilità sarebbe un Writer sostenuta da una ByteArrayOutputStream:

ByteArrayOutputStream bout = new ByteArrayOutputStream(); 
Writer writer = new OutputStreamWriter(bout); 
try { 
    writer.write("String A"); 
    writer.write("String B"); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 
System.out.println(bout.toByteArray()); 

try { 
    writer.write("String C"); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 
System.out.println(bout.toByteArray()); 

Come sempre, il tuo chilometraggio può variare.