1

我认为为了捕获 lambda,需要分配一个对象(无论是它Object[]还是某种abc$Lambda$xyz类型)。无论如何都可以自定义此过程吗?假设我有这个代码:

private void test() {
    int x = 5;
    Supplier<Integer> supplier = () -> x;
    foo(supplier); // potentially passes the supplier to another thread etc.
}

而且我不想分配对象捕获x,而是从池中获取它并填写值;我也知道在某些时候我可以将对象返回到池中。

我可以写

Supplier<Integer> supplier = pool.get(x, v -> v);

并且我可以为不同的参数类型提供专门的版本(因为 usingObject...会进行分配(好吧,分配可能会被转义分析消除......)但这会使代码变得非常不可读。因此我正在寻找一种更像方面的方式。

这样的事情可能吗?


编辑:为了使池的功能更加明显,get可以实现为

class IntHolderSupplier implements Supplier<Integer> {
    int value;
    IntFunction<Integer> func;
    @Override public Integer get() {
        return func.apply(value);
    }        
}

class Pool {
    Supplier<Integer> get(int arg, IntFunction<Integer> func) {
        IntHolderSupplier holder = ...;
        holder.value = arg;
        holder.func = func;
        return holder;
    }
}

对于我想使用的所有可能类型的 lambda,我需要这种具有特定签名的持有者。

也许我通过提供函数使示例变得有点复杂 - 但我想捕捉这样一个事实,即在Supplier.get()调用时可能会对捕获的参数应用额外的计算。

请忽略 int 被装箱的事实,它可以产生分配。

4

1 回答 1

1

“<em>pool capture lambdas”是用词不当。Lambda 表达式是一种获取功能接口实例的技术解决方案。由于您不是池化 lambda 表达式而是池化接口实例,因此放弃了 lambda 表达式的每个技术方面,例如不变性或 JRE/JVM 控制其生命周期的事实,您应该将其命名为“池功能接口实例” .

所以你可以为这些实例实现一个池,就像你可以为任何类型的对象实现一个池一样。这样的池不太可能比为 lambda 表达式创建的 JVM 托管对象执行得更好,但是您可以尝试一下。

这很简单,如果你保持它们不可变,因此,不要尝试将它们重用于不同的值,而只是在再次遇到以前捕获的值时。这是一个Supplier缓存的示例,用于保存最近 100 个遇到的值的供应商:

class SupplierCache {
    static final int SIZE = 100;
    static LinkedHashMap<Object,Supplier<Object>> CACHE =
        new LinkedHashMap<Object, Supplier<Object>>(SIZE, 1f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<Object, Supplier<Object>> eldest) {
            return size() > SIZE;
        }
    };
    @SuppressWarnings("unchecked")
    static <T> Supplier<T> getSupplier(T t) {
        return (Supplier<T>)CACHE.computeIfAbsent(t, key -> () -> key);
    }
}

(添加线程安全,如果你需要的话)。因此,通过替换Supplier<Integer> supplier = () -> x;Supplier<Integer> supplier = SupplierCache.getSupplier(x);将获得缓存功能,并且由于您不必释放它们,因此您不必对其生命周期做出容易出错的假设。

创建一个实现Supplier并返回可变字段值的对象池,以便您可以手动回收实例,如果您只是创建一个实现的普通类并不太难Supplier,但是,您打开一整罐带有手动内存管理的蠕虫包括回收仍在使用的对象的风险。这些对象不能像上面示例中的不可变对象那样共享。并且您将对象分配替换为查找可回收池实例的操作以及在使用后显式放回实例的操作——这没有理由应该更快。

于 2016-08-26T15:32:10.900 回答