Java 8: Applying Stream Map and Filter in One Go

I am writing a parser for a file in Java 8. The file is read with Files.lines

and returns sequential Stream<String>

.

Each row maps 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;
  }
}

      

We can now match each line in the stream with its 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 the values null

that I want to remove:

parseFile(_file).filter(v -> v != null);

      

How can I combine a map / filter operation, as I already know in parseLine/_stream.map

, if a result is needed?

+3


source to share


2 answers


As already pointed out in the comments, the stream will be processed in one pass, so there is no need to change anything. For what you can use flatMap

and 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);
}

      



This way you won't have any values null

.

+9


source


Update for Java 9:

Usage is Stream<Result>

similar to the wrong return type for a function parse()

. A stream can contain many, many values, so the user parse()

must either assume there will be at most one value in the stream, or use something like collect

to retrieve and use the results of an operation.If a parse()

function and its use are separated by only a few lines of code, it could be ok, but if the distance increases, for example in a completely different file for JUnit testing, the contract of the interface is not clear from the return value.

Instead of returning Stream

, it would be a better interface contract to return empty Optional

when no string is needed.

Optional<Result> parse(String _line) {
   ... code here
   Result _result = null;
   if (/* line needed */) {
      /** parse line into result */
   }
   return Optional.ofNullable(_result);
}

      



Unfortunately it is now _stream.map(this::parse)

returning a stream of values Optional

, so with Java 8, you would need to filter and match this with .filter(Optional::isPresent).map(Optional::get)

and the question was looking for a solution that could do this "in one go."

This question was posted 3 years ago. As of Java 9, we now have the option (pun intended) to use a method Optional::stream

, so we can write instead:

public Stream<Result> parseFile(Path _file) {
  return Files.lines(_file)
      .map(this::parse)
      .flatMap(Optional::stream)
}

      

to convert a stream of values Optional

to a stream of values Result

without any empty options.

+1


source







All Articles