Use streams to read specific columns from a file in Java 8 and put them into a two-dimensional array
I have an input file that looks like this
@id1 1.2 3.4
@id2 6.8 8.1
@id3 1.5 9.4
@id4 5.9 2.7
I just want to store the numbers in a two-dimensional array and forget the first column containing @id.
I also want to use a stream just for that operation.
So far, I’ve done two ways:
The first method reads the input file and stores each row in a list, as an array of strings:
private List<String[]> savefromfile(String filePath) throws IOException {
List<String[]> rowsOfFile = new LinkedList<String[]>();
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
lines.forEach(line -> {
String rows[] = line.trim().split("\\s+");
rowsOfFile.add(rows);
});
lines.close();
}
return rowsOfFile;
The second method takes a list as input and returns a two-dimensional array containing only the column numbers:
private double[][] storeAllID(List<String[]> rowsOfFile) {
int numberOfID = rowsOfFile.size();
double[][] allID = new double[numberOfID][2];
int i = 0;
for (String[] line : rowsOfFile) {
double id[] = new double[2];
id[0] = Double.parseDouble(line[1]);
id[1] = Double.parseDouble(line[2]);
allID[i++] = id;
}
return allID;
}
Is there a way to make this code more efficient? I just want a short way to read the input file and return a two-dimensional array containing only numbers.
I don’t think it’s necessary to write 20 lines of code to do this.
Solution
Using a stream in a savefromfile
doesn’t really get any benefit because you’re using it as if it were a normal for loop. To make the code a little more concise, you can get rid of local variables altogether and don’t need to call close()
because you’re already using try-with-resources.
private List<String[]> savefromfile(String filePath) throws IOException {
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
return lines
.map(line -> line.trim().split("\\s+"))
.collect(Collectors.toCollection(LinkedList::new));
}
}
I don’t know why you would want to separate the parsing of double[][] into a separate method, because you can use the map in your stream to do it :
private double[][] loadFromFile(String filePath) throws IOException {
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
return lines
.map(line -> line.trim().split("\\s+"))
.map(line -> new double[] {
Double.parseDouble(line[1]),
Double.parseDouble(line[2])
})
.toArray(double[][]::new);
}
}
For performance, you only need to measure for yourself whether using lower-level data types and loops is worth the added complexity.