将 List<char[]> 转换为 Array char[] 而不使用 System.arraycopy()

2022-09-03 16:37:29

在Java中转换/扁平化a的简单方法是什么?List<char[]>char[]

我知道我可以通过迭代和使用来做到这一点,但我想知道有没有更简单的方法可以使用Java 8流来做到这一点?ListSystem.arraycopy

也许是这样的东西,但不必将基元框为:charCharacter

List<char[]> listOfCharArrays = ...

Character[] charArray =
    Stream.of(listOfCharArrays )
        .flatMap(List::stream)
        .toArray(Character[]::new);

答案 1

这是我能想到的最易读的版本。您可以通过 StringBuilder 将所有 char 数组追加到 String 中,然后将其转换为 .char[]

char[] chars = listOfCharArrays.stream()
    .collect(Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString))
    .toCharArray();

可能比迭代版本慢得多,因为可以复制内存块。arrayCopy

您可以考虑预先计算字符的总数以避免StringBuilder数组重新分配,但是这种优化和任何其他优化都会占用您从使用流中获得的可读性增益。

int totalSize = listOfCharArrays.stream().mapToInt(arr -> arr.length).sum();
char[] chars = listOfCharArrays.stream()
    .collect(Collector.of(() -> new StringBuilder(totalSize), //... the same

有2个不必要的副本(->,->)实际上是这些类不完全适合此任务的结果。 更适合;看看马丁的答案StringBuilderStringStringchar[]CharBuffer


答案 2

我只能想到一件事,那就是使用CharBuffer。出于效率原因,我总是首先计算正确的大小,然后执行复制。任何执行多个副本和/或执行字符串处理的解决方案都将是低效的。

下面是代码。第一行计算所需数组的总大小,然后为其分配足够的内存。第二行使用上述方法执行复制。最后一行返回支持 .putchar[]CharBuffer

CharBuffer fullBuffer = CharBuffer.allocate(
        listOfCharArrays.stream().mapToInt(array -> array.length).sum());
listOfCharArrays.forEach(fullBuffer::put);
char[] asCharArray = fullBuffer.array();

当然,我不能保证它不会在CharBuffer#put方法中的某个地方使用。我强烈期望它将在内部使用或类似的代码。不过,这可能适用于此处提供的大多数解决方案。System.arrayCopySystem.arrayCopy

如果可以估计最大大小,则可以通过使用足够大的缓冲区来避免第一次大小计算,但这需要缓冲区中数据的附加副本;CharBuffer#array 仅返回大小正确的支持数组,这意味着数据只复制一次


如果要使用面向对象的代码,也可以直接使用。请注意,您需要确保在写入它之后翻转它,并且它是可变的(您可以使用or方法传递副本 - 返回的实例引用相同的缓冲区,但具有独立的,可变的“位置”和“限制”字段)。CharBufferCharBufferduplicateasReadOnly

Java NIO类和Java NIO类有点难以理解,但是一旦你这样做,你就会从它们中获得很大的好处,例如,当将它们用于或内存映射文件时。BufferCharEncoder