首页 > 解决方案 > 如何使用 JDK 11 向 Collection.toArray() 提供生成器函数?

问题描述

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

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

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

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

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

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

Collection<String>这工作正常,并返回对应于 的适当长度的 String 数组。

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

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

这是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:[这个,是,一个,列表,的,字符串]

intFunc: 大小: 0

intFunc:要返回的数组:[A, B, C]

array2:[这,是,一个,列表,的,字符串]

intFunc: 大小: 4

intFunc:要返回的数组:[A, B, C, D]

array3:[这个,是,一个,列表,的,字符串]

输出表明我的生成器函数做什么并不重要——它返回的数组没有被使用。

我的问题是如何获得这个新的实现toArray()来使用我的生成器函数返回的数组,或者我是否正在尝试一些不可能的事情?


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

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

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

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

给出以下控制台输出:

intFunc: 大小: 9

intFunc:要返回的数组:[A, B, C, D, E, F, G, H, I]

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

SO 问题Collections emptyList/singleton/singletonList/List/Set toArray解释了为什么返回的数组中有一个空值。

标签: javaeclipse-photonjava-11

解决方案


正如您所指出的,toArray(IntFunction<T[]>)是一个简单转发到的默认方法toArray(T[])(在使用给定函数创建数组之后)。如果您仔细查看该方法,您会找到问题的答案 - 来自JDK 10 Javadoc(重点是我的):

返回一个包含此集合中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。如果集合适合指定的数组,则在其中返回。否则,将使用指定数组的运行时类型和此集合的大小分配一个新数组。

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

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);
}

推荐阅读