【问题标题】:How to convert a Java Stream to a Scala Stream?如何将 Java 流转换为 Scala 流?
【发布时间】:2016-07-12 21:50:51
【问题描述】:

作为将 Java 代码转换为 Scala 代码的工作的一部分,我需要将 Java 流 Files.walk(Paths.get(ROOT)) 转换为 Scala。我无法通过谷歌搜索找到解决方案。 asScala 不会这样做。有什么提示吗?

相关代码如下:

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;

// ...snip...

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()))

Files.walk(Paths.get(ROOT)) 在 Java 中的返回类型是 Stream<Path>

【问题讨论】:

  • 您真正需要什么?在2.12中,您可以stream.filter(x => something)
  • @som-snytt 在应用 Scala 代码之前,我需要先转换 Java 流类型。
  • 因此,按照我的示例,您可以向 Java API 提供 Scala 函数文字。
  • 对于我们这些来自未来的人,从 Scala 2.13+ 开始,您可以使用 import scala.jdk.StreamConverters._; yourJavaStream.toScala(LazyList)

标签: scala java-8 scala-java-interop


【解决方案1】:

有一种更好的方法,不需要@marcospereira here 提到的兼容层或实验性 2.11 功能

基本上只使用一个迭代器:

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

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

【讨论】:

  • 根据java.nio.file.Files#list应该关闭流val stream : Stream[Path] = Files.list(Paths.get("."))val paths: List[Path] = stream.iterator().asScala.toListstream.close()
  • scala.collection.JavaConverters 已弃用,建议改为导入 scala.jdk.CollectionConverters._
【解决方案2】:

Scala 2.13 开始,标准库包括 scala.jdk.StreamConverters,它提供 Java 到 Scala 的隐式流转换:

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>

注意LazyList(而不是Stream)的用法,因为Streams 已在Scala 2.13 中重命名为LazyList

【讨论】:

【解决方案3】:

Java 8 Streams 和 Scala Streams 在概念上是不同的东西; Java 8 Stream 不是一个集合,所以通常的集合转换器不起作用。您可以使用scala-java8-compat (github) 库将toScala 方法添加到Java Streams:

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

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

您不能真正从 Java 中使用这种转换 (Java->Scala),因此如果您必须从 Java 中执行此操作,那么只运行流并自己构建 Scala 流会更容易(但仍然很尴尬)(这是前面提到的库在幕后所做的):

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();

但是,这两种方式都将完全消耗 Java Stream,因此您无法通过将其转换为 Scala Stream 来获得惰性求值的好处,还不如直接将其转换为 Vector。如果您只想使用 Scala 函数文字语法,有不同的方法可以实现这一点。您可以使用同一个库来使用函数转换器,类似于集合转换器:

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)

或者,自 2.11 起,Scala 在 -Xexperimental 标志下对 SAM 类型提供了实验性支持。在 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

【讨论】:

    猜你喜欢
    • 2017-01-31
    • 2019-04-12
    • 1970-01-01
    • 2018-07-04
    • 2017-01-20
    • 2023-03-10
    • 2016-01-19
    • 1970-01-01
    相关资源
    最近更新 更多