如何在Kotlin中的Java 8 Stream上调用collect(Collectors.toList())? [英] How can I call collect(Collectors.toList()) on a Java 8 Stream in Kotlin?

查看:2863
本文介绍了如何在Kotlin中的Java 8 Stream上调用collect(Collectors.toList())?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码:

directoryChooser.title = "Select the directory"
val file = directoryChooser.showDialog(null)
if (file != null) {
    var files = Files.list(file.toPath())
            .filter { f ->
                f.fileName.endsWith("zip") && f.fileName.endsWith("ZIP")
                        && (f.fileName.startsWith("1207") || f.fileName.startsWith("4407") || f.fileName.startsWith("1507") || f.fileName.startsWith("9007") || f.fileName.startsWith("1807"))
            }
    for (f in files) {
        textArea.appendText(f.toString() + "\n")
    }
}

如果我打电话给 collect(Collectors.toList( ))在过滤结束时,我得到:

If I call collect(Collectors.toList()) at the end of filter, I get:

Error:(22, 13) Kotlin: [Internal Error] org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: no descriptor for type constructor of ('Captured(in ('Path'..'Path?'))'..'CapturedTypeConstructor(in ('Path'..'Path?'))?')
Cause: no descriptor for type constructor of ('Captured(in ('Path'..'Path?'))'..'CapturedTypeConstructor(in ('Path'..'Path?'))?')
File being compiled and position: (22,13) in D:/My/devel/ListOfReestrs/src/Controller.kt
PsiElement: var files = Files.list(file.toPath())
                    .filter { f ->
                        f.fileName.endsWith("zip") && f.fileName.endsWith("ZIP")
                                && (f.fileName.startsWith("1207") || f.fileName.startsWith("4407") || f.fileName.startsWith("1507") || f.fileName.startsWith("9007") || f.fileName.startsWith("1807"))
                    }.collect(Collectors.toList())
The root cause was thrown at: JetTypeMapper.java:430

如果我不这样做,我会得到 f ,其类型为 [错误:错误] 在我的for循环中。

If I don't do this, I get the f with the type [error: Error] in my for-loop.

推荐答案

UPDATE:此问题现已在Kotlin 1.0.1中修复(以前是 KT -5190 )。无需解决问题。

UPDATE: This issue is now fixed in Kotlin 1.0.1 (previously was KT-5190). No work around is needed.

解决方法#1:

创建此扩展函数,然后将其简单地用作 .toList()上的code>流:

Create this extension function, then use it simply as .toList() on the Stream:

fun <T: Any> Stream<T>.toList(): List<T> = this.collect(Collectors.toList<T>())

用法:

Files.list(Paths.get(file)).filter { /* filter clause */ }.toList()

这为 Collectors.toList()添加了更明确的泛型参数调用,防止在推理泛型期间发生的错误(对于该方法返回类型有点复杂收集器< T,?,列表< T>> eeeks!?!)。

This adds a more explicit generic parameter to the Collectors.toList() call, preventing the bug which occurs during inference of the generics (which are somewhat convoluted for that method return type Collector<T, ?, List<T>>, eeeks!?!).

解决方法#2:

为您的调用添加正确的类型参数 Collectors.toList< Path>()以避免该参数的类型推断:

Add the correct type parameter to your call as Collectors.toList<Path>() to avoid type inference of that parameter:

Files.list(Paths.get(file)).filter { /* filter clause */ }.collect(Collectors.toList<Path>())

但是变通方法#1中的扩展功能更易于使用且更简洁。

But the extension function in workaround #1 is more re-usable and more concise.

另一种解决方法是不收集 Stream 。您可以保持懒惰,并将 Stream 转换为Kotlin 序列 Iterator ,这是一个扩展函数,用于制作序列

Another way around the bug is to not collect the Stream. You can stay lazy, and convert the Stream to a Kotlin Sequence or Iterator, here is an extension function for making a Sequence:

fun <T: Any> Stream<T>.asSequence(): Sequence<T> = this.iterator().asSequence()

现在你有 forEach 以及许多其他可用的功能,同时仍然懒得只消耗 Stream 一次。使用 myStream.iterator()是另一种方式,但可能没有 Sequence 那么多的功能。

Now you have forEach and many other functions available to you while still consuming the Stream lazily and only once. Using myStream.iterator() is another way but may not have as much functionality as a Sequence.

当然,在序列的某些处理结束时,您可以 toList() toSet()或使用任何其他Kotlin扩展来更改集合类型。

And of course at the end of some processing on the Sequence you can toList() or toSet() or use any other of the Kotlin extensions for changing collection types.

和有了这个,我会为列出文件创建一个扩展,以避免错误的API设计路径路径文件文件

And with this, I would create an extensions for listing files to avoid the bad API design of Paths, Path, Files, File:

fun Path.list(): Sequence<Path> = Files.list(this).iterator().asSequence()

这至少会很好地流动从左到右:

which would at least flow nicely from left to right:

File(someDir).toPath().list().forEach { println(it) }
Paths.get(dirname).list().forEach { println(it) }






使用Java 8 Streams的替代方法:



我们可以稍微更改您的代码以从文件,而你最后只需使用 toList()


Alternatives to using Java 8 Streams:

We can change your code slightly to get the file list from File instead, and you would just use toList() at the end:

file.listFiles().filter { /* filter clause */ }.toList()

file.listFiles { file, name ->  /* filter clause */ }.toList()

不幸的是文件。您最初使用的list(...)会返回 Stream ,并且不会让您有机会使用传统的集合。此更改通过启动返回数组或集合的函数来避免这种情况。

Unfortunately the Files.list(...) that you originally used returns a Stream and doesn't give you the opportunity to use a traditional collection. This change avoids that by starting with a function that returns an Array or collection.

一般情况:

在大多数情况下,您可以避免使用Java 8流,并使用本机Kotlin stdlib函数和Java集合的扩展。 Kotlin确实使用Java集合,通过编译时只读和可变接口。但随后它添加了扩展功能以提供更多功能。因此,您具有相同的性能,但具有更多功能。

In most cases you can avoid Java 8 streams, and use native Kotlin stdlib functions and extensions to Java collections. Kotlin does indeed use Java collections, via compile-time readonly and mutable interfaces. But then it adds extension functions to provide more functionality. Therefore you have the same performance but with many more capabilities.

另见:

  • What Java 8 Stream.collect equivalents are available in the standard Kotlin library? - You will see it is more concise to use Kotlin stdlib functions and extensions.
  • Kotlin Collections, and Extension Functions API docs
  • Kotlin Sequences API docs
  • Kotlin idioms

您应该查看 API参考,了解stdlib中可用的内容。

You should review the API reference for knowing what is available in the stdlib.

这篇关于如何在Kotlin中的Java 8 Stream上调用collect(Collectors.toList())?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆