如何使用 JDK 11 为 Collection.toArray() 提供生成器函数?

How do I provide a generator function to Collection.toArray() using JDK 11?

我已经升级了 Eclipse Photon 4.8 (http://download.eclipse.org/eclipse/downloads/drops4/S-4.9M2-201808012000/) to support JDK 11 (https://marketplace.eclipse.org/content/java-11-support-eclipse-photon-49)。它似乎工作正常(版本:4.9 构建 ID:I20180801-2000).

在 JDK 11 中有一个新的覆盖方法 toArray() 在 Java.util.Collection:

default <T> T[] toArray(IntFunction<T[]> generator) {
    return toArray(generator.apply(0));
}

这是默认方法,但未被覆盖。它所做的只是将由提供的生成器函数(使用零的硬编码参数)编辑的值 return 传递给另一个 toArray() 的重写,然后 returns 集合的内容作为数组。

如该方法的 Javadoc 中所述,可以这样调用:

String[] y = x.toArray(String[]::new);

这很好用,适当长度的字符串数组,对应于 Collection<String>,是 returned。

Javadoc 还声明“默认实现使用零调用生成器函数,然后将结果数组传递给 toArray(T[])”。

如果我提供自己的生成器函数,它确实会被调用(如 println() 控制台输出所示),但其 apply() 方法的 return 值似乎被忽略.就好像我调用了 toArray(String[]::new) 而不管数组 return 由我的生成器函数编辑的内容。

这是 MCVE:

package pkg;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.IntFunction;

public class App {
    public static void main(String[] args) {

        IntFunction<String[]> intFunc = (int sz) -> {
            System.out.println("intFunc: sz: " + sz);
            if (sz == 0) {
                sz = 3;
            }
            String[] array = new String[sz];
            for (int i = 0; i < sz; i++) {
                array[i] = Character.toString('A' + i);

            }
            System.out.println("intFunc: array to be returned: " + Arrays.toString(array));
            return array;
        };

        Collection<String> coll = List.of("This", "is", "a", "list", "of", "strings");

        // Correctly returns the collection as an array, as described in JDK11 Javadoc.
        String[] array1 = coll.toArray(String[]::new);
        System.out.println("array1: " + Arrays.toString(array1) + '\n');

        // Use generator function to return a different collection as an array - doesn't work.      
        String[] array2 = coll.toArray(intFunc);
        System.out.println("array2: " + Arrays.toString(array2) + '\n');

        // Use generator function to return a different collection as an array - doesn't work.
        String[] array3 = coll.toArray(intFunc.apply(coll.size()-2));
        System.out.println("array3: " + Arrays.toString(array3));
    }
}

这是 运行 MCVE 产生的控制台输出:

array1: [This, is, a, list, of, strings]

intFunc: sz: 0

intFunc: array to be returned: [A, B, C]

array2: [This, is, a, list, of, strings]

intFunc: sz: 4

intFunc: array to be returned: [A, B, C, D]

array3: [This, is, a, list, of, strings]

输出显示我的生成器函数做什么并不重要 - returns 未使用的数组。

我的问题是如何让 toArray() 的新实现使用由我的生成器函数编辑的数组 return,或者我是在尝试一些不可能的事情吗?


根据评论和 Nicolai 的回答更新:

我的示例代码的问题不在于生成器,而在于我的测试用例。它们碰巧导致生成器 return 一个元素少于集合的数组,因此分配了一个新数组,以准确保存集合中元素的数量。

一个 return 数组大于集合的测试用例按预期工作。例如这段代码:

    String[] array4 = coll.toArray(intFunc.apply(coll.size() + 3));
    System.out.println("array4: " + Arrays.toString(array4));

给出以下控制台输出:

intFunc: sz: 9

intFunc: array to be returned: [A, B, C, D, E, F, G, H, I]

array4: [This, is, a, list, of, strings, null, H, I]

SO 问题 解释了为什么 returned 数组中存在空值。

正如您所指出的,toArray(IntFunction<T[]>)a default method that simply forwards to toArray(T[]) (after creating an array with the given function). If you take a closer look at that method, you will find the answer to your question - from the JDK 10 Javadoc(强调我的):

Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array. If the collection fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this collection.

要使用您创建的数组,它必须足够长以容纳集合的元素,例如:

public static void main(String[] args) {
    var createdArray = new AtomicReference<String[]>();
    var usedArray = List.of("A", "B", "C").toArray(__ -> {
        createdArray.set(new String[5]);
        return createdArray.get();
    });

    var message = String.format(
            "%s (length: %d; identical with created array: %s)",
            Arrays.toString(usedArray), usedArray.length, usedArray == createdArray.get());
    System.out.println(message);
}

生成器函数返回的数组未被忽略。使用返回数组的组件类型。假设您有一个字符串集合,如您的示例所示:

jshell> Collection<String> coll = List.of("This", "is", "a", "list", "of", "strings")
coll ==> [This, is, a, list, of, strings]

你的生成器函数是这样的:

jshell> IntFunction<CharSequence[]> g = x -> {
   ...>   var a = new CharSequence[x];
   ...>   System.out.println(a);
   ...>   return a;
   ...> }
g ==> $Lambda/0x00000008000d8040@17d677df

(请注意,这会打印出数组的默认表示形式,其中包括其标识哈希码,而不是数组的内容。数组的内容符合预期。)

然后您可以将集合中的字符串复制到 CharSequence 数组中:

jshell> System.out.println(coll.toArray(g))
[Ljava.lang.CharSequence;@7d70d1b1
[Ljava.lang.CharSequence;@2a742aa2

现在假设源是一个空集合:

jshell> System.out.println(List.of().toArray(g))
[Ljava.lang.CharSequence;@3dfc5fb8
[Ljava.lang.CharSequence;@3dfc5fb8

您可以看到生成器返回的零大小数组的大小足以包含零个元素,因此直接返回。