I am writing a parser for a file in Java 8. The file is read using Files.lines
and returns a sequential Stream<String>
.
Each line is mapped to a data object Result
like this:
Result parse(String _line) {
// ... code here
Result _result = new Result().
if (/* line is not needed */) {
return null;
} else {
/* parse line into result */
return _result;
}
}
Now we can map each line in the stream to its according result:
public Stream<Result> parseFile(Path _file) {
Stream<String> _stream = Files.lines(_file);
Stream<Result> _resultStream = _stream.map(this::parse);
}
However the stream now contains null
values which I want to remove:
parseFile(_file).filter(v -> v != null);
How can I combine the map/filter operation, as I already know in parseLine/_stream.map
if the result is needed?
As already pointed out in the comments the stream will be processed in one pass, so there isn't really a need to change anything. For what it's worth you could use flatMap
and let parse
return a stream:
Stream<Result> parse(String _line) {
.. code here
Result _result = new Result().
if (/* line is not needed */) {
return Stream.empty();
} else {
/** parse line into result */
return Stream.of(_result);
}
}
public Stream<Result> parseFile(Path _file) {
return Files.lines(_file)
.flatMap(this::parse);
}
That way you won't have any null
values in the first place.
Updating for Java 9:
Using Stream<Result>
seems like the wrong return type for the parse()
function. A stream can contain many, many values, so the user of parse()
either has to assume there will be at most one value in the stream, or use something like collect
to extract and use the results of the parse()
operation. If the function and its usage are only separated by a few lines of code, this may be fine, but if the distance increases, such as in a completely different file for JUnit testing, the interface contract isn't clear from the return value.
Instead of returning a Stream
, it would be a better interface contract to return an empty Optional
when the line is not needed.
Optional<Result> parse(String _line) {
... code here
Result _result = null;
if (/* line needed */) {
/** parse line into result */
}
return Optional.ofNullable(_result);
}
Unfortunately, now _stream.map(this::parse)
returns a stream of Optional
values, so with Java 8, again you'd need to filter and map this with .filter(Optional::isPresent).map(Optional::get)
, and the question was looking for a solution which could do this "in one go".
This question was posted 3 years ago. With Java 9, we now have the option (pun intended) of using the Optional::stream
method, so we can instead write:
public Stream<Result> parseFile(Path _file) {
return Files.lines(_file)
.map(this::parse)
.flatMap(Optional::stream)
}
to transform the stream of Optional
values into a stream of Result
values, without any of the empty optionals.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With