How to convert a Java Stream to a Scala Stream?

試著忘記壹切 提交于 2019-11-27 11:15:33

问题


As a part of an effort of converting Java code to Scala code, I need to convert the Java stream Files.walk(Paths.get(ROOT)) to Scala. I can't find a solution by googling. asScala won't do it. Any hints?

The followings is the related code:

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Collectors;

....
   Files.walk(Paths.get(ROOT))
            .filter(path -> !path.equals(Paths.get(ROOT)))
            .map(path -> Paths.get(ROOT).relativize(path))
            .map(path -> linkTo(methodOn(FileUploadController.class).getFile(path.toString())).withRel(path.toString()))
            .collect(Collectors.toList()))

where the Files.walk(Paths.get(ROOT)) return type is Stream<Path> in Java.


回答1:


There is a slightly nicer way without needing the compat layer or experimental 2.11 features mentioned here by @marcospereira

Basically just use an iterator:

import java.nio.file.{Files, Paths}
import scala.collection.JavaConverters._

Files.list(Paths.get(".")).iterator().asScala



回答2:


Java 8 Streams and Scala Streams are conceptually different things; the Java 8 Stream is not a collection, so the usual collection converter won't work. You can use the scala-java8-compat (github) library to add a toScala method to Java Streams:

import scala.compat.java8.StreamConverters._
import java.nio.file.{ Files, Path, Paths }

val scalaStream: Stream[Path] = Files.walk(Paths.get(".")).toScala[Stream]

You can't really use this conversion (Java->Scala) from Java, so if you have to do this from Java, it's easier (but still awkward) to just run the stream and build the Scala Stream yourself (which is what the aforementioned library is doing under the hood):

import scala.collection.immutable.Stream$;
import scala.collection.mutable.Builder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

final Stream<Path> stream = Files.walk(Paths.get("."));
final Builder<Path, scala.collection.immutable.Stream<Path>> builder = Stream$.MODULE$.newBuilder();
stream.forEachOrdered(builder::$plus$eq);
final scala.collection.immutable.Stream<Path> result = builder.result();

However, both ways will fully consume the Java Stream, so you don't get the benefit of the lazy evaluation by converting it to a Scala Stream and might as well just convert it directly to a Vector. If you just want to use the Scala function literal syntax, there different ways to achieve this. You could use the same library to use function converters, similar to collection converters:

import scala.compat.java8.FunctionConverters._
import java.nio.file.{ Files, Path, Paths }

val p: Path => Boolean = p => Files.isExecutable(p)
val stream: java.util.stream.Stream[Path] = Files.walk(Paths.get(".")).filter(p.asJava)

Alternatively since 2.11, Scala has experimental support for SAM types under the -Xexperimental flag. This will be non-experimental without a flag in 2.12.

$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.

scala> import java.nio.file.{ Files, Path, Paths }
import java.nio.file.{Files, Path, Paths}

scala> Files.walk(Paths.get(".")).filter(p => Files.isExecutable(p))
<console>:13: error: missing parameter type
       Files.walk(Paths.get(".")).filter(p => Files.isExecutable(p))
                                         ^

scala> :set -Xexperimental

scala> Files.walk(Paths.get(".")).filter(p => Files.isExecutable(p))
res1: java.util.stream.Stream[java.nio.file.Path] = java.util.stream.ReferencePipeline$2@589838eb

scala> Files.walk(Paths.get(".")).filter(Files.isExecutable)
res2: java.util.stream.Stream[java.nio.file.Path] = java.util.stream.ReferencePipeline$2@185d8b6



回答3:


Starting Scala 2.13, the standard library includes scala.jdk.StreamConverters which provides Java to Scala implicit stream conversions:

import scala.jdk.StreamConverters._

val javaStream = Files.walk(Paths.get("."))
// javaStream: java.util.stream.Stream[java.nio.file.Path] = java.util.stream.ReferencePipeline$3@51b1d486
javaStream.toScala(LazyList)
// scala.collection.immutable.LazyList[java.nio.file.Path] = LazyList(?)
javaStream.toScala(Iterator)
// Iterator[java.nio.file.Path] = <iterator>

Note the usage of LazyList (as opposed to Stream) as Streams have been renamed LazyList in Scala 2.13.



来源:https://stackoverflow.com/questions/38339440/how-to-convert-a-java-stream-to-a-scala-stream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!