Как преобразовать поток Java в поток Scala?

В рамках усилий по преобразованию кода 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;

....
   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)) равен Stream<Path> в Java.


person TeeKai    schedule 12.07.2016    source источник
comment
Что вам действительно нужно? В версии 2.12 вы можете stream.filter(x => something).   -  person som-snytt    schedule 13.07.2016
comment
@som-snytt Мне нужно сначала преобразовать тип потока Java, прежде чем применять код Scala.   -  person TeeKai    schedule 13.07.2016
comment
Итак, согласно моему примеру, вы можете предоставить литерал функции Scala для Java API.   -  person som-snytt    schedule 13.07.2016


Ответы (3)


Существует немного более приятный способ, не требующий уровня совместимости или экспериментальных функций 2.11, упомянутых здесь @marcospereira

В основном просто используйте итератор:

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

Files.list(Paths.get(".")).iterator().asScala
person mirosval    schedule 04.08.2016
comment
Согласно java.nio.file.Files#list поток должен быть закрыт val stream : Stream[Path] = Files.list(Paths.get(".")) val paths: List[Path] = stream.iterator().asScala.toList stream.close() - person Ilya Bystrov; 01.12.2018
comment
scala.collection.JavaConverters устарел, вместо него рекомендуется импортировать scala.jdk.CollectionConverters._. - person ComFreek; 05.05.2020

Начиная с 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), так как Stream были переименованы в LazyList в Scala 2.13.

person Xavier Guihot    schedule 29.03.2019

Java 8 Streams и Scala Streams концептуально разные вещи; поток Java 8 не является коллекцией, поэтому обычный преобразователь коллекций не будет работать. Вы можете использовать scala-java8-compat (github), чтобы добавить toScala метод для потоков Java:

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

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

На самом деле вы не можете использовать это преобразование (Java->Scala) из Java, поэтому, если вам нужно сделать это из 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 имеет экспериментальную поддержку типов SAM под флагом -Xexperimental. Это будет не экспериментально без флага в 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
person knutwalker    schedule 13.07.2016