Как мне предоставить функцию генератора для Collection.toArray (), используя JDK 11? - PullRequest
0 голосов
/ 28 августа 2018

Я обновил 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) Идентификатор сборки: I20180801-2000).

В JDK 11 есть новое переопределение метода toArray() в Java.util.Collection:

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

Это метод по умолчанию, но он не переопределяется. Все, что он делает, это передает значение, возвращаемое предоставленной функцией генератора (используя жестко закодированный аргумент, равный нулю), в другое переопределение toArray(), которое затем возвращает содержимое коллекции в виде массива.

Как описано в Javadoc для этого метода, его можно назвать так:

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

Это прекрасно работает, и возвращается массив String соответствующей длины, соответствующей Collection<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: [Это список строк, из которых] a

intFunc: sz: 0

intFunc: возвращаемый массив: [A, B, C]

array2: [Это список строк, из которых] a

intFunc: sz: 4

intFunc: возвращаемый массив: [A, B, C, D]

array3: [Это список строк, из которых] a

Вывод показывает, что не имеет значения, что делает моя функция генератора - возвращаемый массив не используется.

Мой вопрос: как мне заставить эту новую реализацию toArray() использовать массив, возвращенный моей функцией генератора, или я пытаюсь сделать что-то невозможное?


Обновление на основе комментариев и ответа от Николая:

Проблема с моим примером кода была не с генератором, а с моими тестовыми примерами. Они оказались причиной того, что генератор вернул массив с меньшим количеством элементов, чем в коллекции, поэтому вместо него был выделен новый массив, в котором будет точно храниться количество элементов в коллекции.

Тестовый пример, который возвращает массив больше, чем коллекция, работает, как ожидалось. Например этот код:

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

дает следующий вывод консоли:

intFunc: sz: 9

intFunc: возвращаемый массив: [A, B, C, D, E, F, G, H, I]

array4: [Это список строк, null, H, I]

Вопрос SO Коллекции emptyList / singleton / singletonList / List / Set toArray объясняет, почему в возвращаемом массиве есть нулевое значение.

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Массив, возвращаемый функцией генератора, не игнорируется. Используется тип компонента возвращаемого массива. Предположим, у вас есть коллекция строк, как в вашем примере:

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$28/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

Вы можете видеть, что массив нулевого размера, возвращаемый генератором, имеет достаточный размер, чтобы содержать нулевые элементы, поэтому он просто возвращается.

0 голосов
/ 28 августа 2018

Как вы указали, 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);
}
...