Kotlin序列转换为数组

14

如何在 Kotlin 中高效地将 Sequence 转换为 Array (或原始数组,例如 IntArray)?

我发现 Sequence 没有 toArray() 方法。而 toList().toArray()toList().toIntArray())会创建额外的临时列表。


toList().toArray()对您不起作用了吗? - Zaid Mirza
我正在处理非常大的数据(需要数百GB的内存),我不想增加额外的空间和时间开销。 - Shreck Ye
1
我已经在 https://youtrack.jetbrains.com/issue/KT-31260#focus=streamItem-27-3716001.0-0 添加了额外的示例。 - delitescere
3个回答

10

没有toArray()方法,因为与列表不同,序列不允许查找其包含的元素数量(实际上可以是无限的),因此不可能分配正确大小的数组。

如果您在特定情况下了解序列的某些信息,则可以通过手动从序列复制元素到数组并分配数组来编写更高效的实现。例如,如果已知大小,则可以使用以下函数:

fun Sequence<Int>.toIntArray(size: Int): IntArray {
    val iter = iterator()
    return IntArray(size) { iter.next() }
}

假设我知道大小,是否有一种简单的扩展方法,类似于 toArray(size: Int) (toIntArray(size: Int)),用于“分配一个数组并将序列中的元素复制到数组中”? - Shreck Ye
或者至少有没有比“手动”更简单的函数式方法? - Shreck Ye
1
Sequence<T>确实有toList()方法,但它也需要知道元素的数量,因此第一段的推理是值得怀疑的。看起来他们本可以在旁边添加toArray()方法。 - Hakanai
@Hakanai,您指的是哪种列表类型?大多数List接口的实现不需要您事先知道元素数量。特别是,ArrayList从一个容量开始,但随着需要而增长。LinkedList根本不关心。数组无法在没有大小的情况下构建。假设的toArray可以创建任意大小的数组,然后在迭代序列时按需分配新的(更大的)数组,但那将重新实现ArrayList - 我看不出有什么意义。但是,对于某些人来说,toArray(size: Int)可能会很有用。 - Joffrey
1
@Joffrey 我不理解这个论点。他们可以使用ArrayList来实现它。甚至可以将其实现为 toList().toXArray()。重点是,Kotlin的Sequence<T>没有将序列转换为数组的方法,而Java的Stream<T>则有。这是Kotlin API中的一个空缺,预计在某个时间点会被填补,但现在它是一个明显的疏漏。 - Hakanai
@Hakanai 这是一个公正的观点,尽管我认为大多数情况下,看到 toArray() 方法的人会期望它 经过列表,否则他们可能会直接使用列表。数组在一般情况下使用起来不太方便,在 Kotlin 中也不太常见,除非你有特定的性能需求,即使在这种情况下,使用 ArrayList 实现的 toArray 也会非常糟糕。因此,我认为添加这样的 API 没有太多好处(特别是因为它会让用户误以为它会直接收集到一个数组中)。 - Joffrey

1

我希望为Sequence<T>添加一个版本。使用refied T(需要函数是内联的)来创建正确类型的数组,在Java中不可能实现:D

inline fun <reified T> Sequence<T>.toArray(size: Int): Array<T> {
  val iter = iterator()
  return Array(size) { iter.next() }
}

我不同意你的观点,因为 Array<T> 会创建一个低效的对象数组而不是原始数组。 - Shreck Ye

0

因为我不能写评论,这是原始类型最高答案的另一个版本。

fun <I, O> Sequence<I>.toArray(
    size: Int,
    factory: (size: Int, init: (Int) -> I) -> O
): O {
    val iter = iterator()
    return factory(size) { iter.next() }
}

//usage
seq<Byte>
  .toArray(size, ::ByteArray)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接