Если список маленький Object[]
var handle = MethodHandles.identity(Object[].class);
// (Object,Object...Object) -> Object[]
handle = handle.asCollector(Object[].class, elements.size());
// (InternalContext,InternalContext..InternalContext) -> Object[]
handle = MethodHandles.filterArguments(handle, 0, elements.toArray(new MethodHandle[0]));
// (InternalContext) -> Object[]
handle = MethodHandles.permuteArguments(
handle, methodType(Object[].class, InternalContext.class), new int[elements.size()]);
return handle;
}
[/code]
Однако, если список больше, Ascollector () вызов потерпит неудачу, так как результирующая ручка будет иметь слишком много параметров.
Поэтому я попробовал что -то подобное:
Код: Выделить всё
private static final int MAX_ARITY = 255;
static MethodHandle buildLargeArrayNaive(List elementFactories) {
if (elementFactories.size() < MAX_ARITY) {
return makeArrayUsingCollector(elementFactories);
}
var setter = MethodHandles.arrayElementSetter(Object[].class);
// (Object[], InternalContext) -> void
MethodHandle handle = null;
for (int i = 0; i < elementFactories.size(); i++) {
// (Object[], InternalContext) -> void
var setElement =
MethodHandles.filterArguments(
MethodHandles.insertArguments(setter, 1, i), 1, elementFactories.get(i));
if (handle == null) {
handle = setElement;
} else {
handle = MethodHandles.foldArguments(setElement, handle);
}
}
// (Object[], InternalContext) -> Object[]
handle =
MethodHandles.foldArguments(
MethodHandles.dropArguments(
MethodHandles.identity(Object[].class), 1, InternalContext.class),
handle);
return MethodHandles.foldArguments(
handle,
MethodHandles.insertArguments(
MethodHandles.arrayConstructor(Object[].class), 0, elementFactories.size()));
}
Это работает, но полученный метод производит Stackoverflower при конструировании больших массивов (скажем, 100K Elements). Из документов on foldarguments это имеет смысл, поскольку каждый вызов устанавливает структуру «хвостового вызова».
Код: Выделить всё
static makeArray(InternalContext ctx) {
var a = new Object[
Я обнаружил, что могу решить это с помощью рекурсии < /p>
static MethodHandle buildLargeArrayRecursive(List elementFactories) {
// (Object[], InternalContext) -> void
var handle = doBuildLargeArrayRecursive(0, elementFactories);
// (Object[], InternalContext) -> Object[]
handle =
MethodHandles.foldArguments(
MethodHandles.dropArguments(
MethodHandles.identity(Object[].class), 1, InternalContext.class),
handle);
;
return MethodHandles.foldArguments(
handle,
MethodHandles.insertArguments(
MethodHandles.arrayConstructor(Object[].class), 0, elementFactories.size()));
}
static final MethodHandle ARRAY_SETTER = MethodHandles.arrayElementSetter(Object[].class);
static MethodHandle doBuildLargeArrayRecursive(int offset, List elementFactories) {
int size = elementFactories.size();
if (size == 0) {
return MethodHandles.empty(methodType(void.class, Object[].class, InternalContext.class));
}
if (size == 1) {
return MethodHandles.filterArguments(
MethodHandles.insertArguments(ARRAY_SETTER, 1, offset), 1, elementFactories.get(0));
}
int half = size / 2;
var left = elementFactories.subList(0, half);
var right = elementFactories.subList(half, size);
return MethodHandles.foldArguments(
doBuildLargeArrayRecursive(offset + half, right), doBuildLargeArrayRecursive(offset, left));
}
< /code>
, который, по -видимому, масштабируется как минимум до 100 тыс. Элементов. Но ясно, что это будет потреблять пространство стека O (logn). Это, вероятно, хорошо, но это также много вызовов методов ... < /p>
Несколько вещей произошли, уставившись на это: < /p>
[list]
[*] Должен ли я создавать вспомогательные методы для обработки более базовых случаев? например static void set4 (object [], int смещение, объект O1 ... объект O3) {...} [/list]
Подробнее здесь: https://stackoverflow.com/questions/795 ... hodhandles