2015-06-13 7 views
6

Ho un file di testo come questo:Come creare un array bidimensionale da uno stream in Java 8?

ids.txt

1000 
999 
745 
123 
... 

voglio leggere questo file e caricarlo in un array bidimensionale. Mi aspetto di avere una matrice simile a quella riportata di seguito:

Object[][] data = new Object[][] { // 
    { new Integer(1000) }, // 
    { new Integer(999) }, // 
    { new Integer(745) }, // 
    { new Integer(123) }, // 
    ... 
}; 

Ecco il codice che ho scritto:

File idsFile = ... ; 
try (Stream<String> idsStream = Files.lines(idsFile.toPath(), StandardCharsets.US_ASCII)) { 
    Object[][] ids = idsStream 
     .filter(s -> s.trim().length() > 0) 
     .toArray(size -> new Object[size][]); 

    // Process ids array here... 
} 

Quando si esegue questo codice, viene sollevata un'eccezione:

java.lang.ArrayStoreException: null 
at java.lang.System.arraycopy(Native Method) ~[na:1.8.0_45] 
at java.util.stream.SpinedBuffer.copyInto(Unknown Source) ~[na:1.8.0_45] 
at java.util.stream.Nodes$SpinedNodeBuilder.copyInto(Unknown Source) ~[na:1.8.0_45] 
at java.util.stream.SpinedBuffer.asArray(Unknown Source) ~[na:1.8.0_45] 
at java.util.stream.Nodes$SpinedNodeBuilder.asArray(Unknown Source) ~[na:1.8.0_45] 
at java.util.stream.ReferencePipeline.toArray(Unknown Source) ~[na:1.8.0_45] 
... 

Come può risolvere questa eccezione?

+0

Perché 'new Integer (1000)'? E vuoi un '[] []' dove l'array interno ha sempre 'length == 1'? –

+0

@BoristheSpider Gli ID vengono passati a una libreria di terze parti che accetta solo 'Object [] []' come input. – Stephan

risposta

11

dato un Stream<String> è possibile analizzare ogni elemento per un int e avvolgerlo in un Object[] utilizzando:

strings 
     .filter(s -> s.trim().length() > 0) 
     .map(Integer::parseInt) 
     .map(i -> new Object[]{i}) 

Ora per trasformare quel risultato in un Object[][] puoi semplicemente fare:

Object[][] result = strings 
     .filter(s -> s.trim().length() > 0) 
     .map(Integer::parseInt) 
     .map(i -> new Object[]{i}) 
     .toArray(Object[][]::new); 

Per l'ingresso:

final Stream<String> strings = Stream.of("1000", "999", "745", "123"); 

uscita:

[[1000], [999], [745], [123]] 
+5

Se si sospetta che le stringhe possano sporadicamente avere spazi prima e dopo, allora è meglio aggiungere il passo 'trim' separato:' strings.map (String :: trim) .filter (s ->! S.isEmpty()) ' . Inoltre userei 'Integer :: valueOf' invece di' Integer :: parseInt' perché corrisponde meglio al tipo voluto ('Integer', non' int'). Anche se probabilmente è una questione di gusti. –

+0

@TagirValeev Perché è "meglio aggiungere il passaggio di ritaglio separato"? – Stephan

+2

@Stephan: in questo modo è possibile analizzare i file che contengono spazi dopo i numeri (come '" 1 \ n2 \ n3 \ n "'). Attualmente avrai 'NumberFormatException' su questi file. –

5

L'ultima riga deve essere probabilmente size -> new Object[size], ma è necessario fornire array di numeri interi di dimensione uno e sarà necessario anche analizzare le stringhe in Numeri interi.

suggerisco il seguente:

try (Stream<String> idsStream = Files.lines(idsFile.toPath(), StandardCharsets.US_ASCII)) { 
    Object[][] ids = idsStream 
     .map(String::trim) 
     .filter(s -> !s.isEmpty()) 
     .map(Integer::valueOf) 
     .map(i -> new Integer[] { i }) 
     .toArray(Object[][]::new); 

    // Process ids array here... 
}