我知道为了捕获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);
我可以为不同的参数类型设置专门的版本(因为使用Object...
会进行分配(好吧,有可能通过转义分析来消除分配...)但这会呈现代码非常难以理解。因此,我正在寻找一种更像方面的方式。
这样的事情可能吗?
编辑:为了使游泳池的功能更加明显,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;
}
}
我需要这样的持有者,我想使用所有可能类型的lambdas特定签名。
也许我通过提供函数使示例复杂化了一些 - 但我想捕获这样一个事实:在Supplier.get()
调用时可能会对捕获的参数应用额外的计算。
请忽略这样一个事实,即int是盒装的,可以产生分配。
答案 0 :(得分:1)
“池捕捉lambdas ”是用词不当。 Lambda表达式是获取功能接口实例的技术解决方案。由于您不汇集lambda表达式而是接口实例,删除lambda表达式的每个技术方面,如不变性或JRE / JVM控制其生命周期的事实,您应该将其命名为“池功能接口实例< / EM>”。
因此,您可以为这些实例实现池,就像您可以为任何类型的对象实现池一样。这种池的性能不太可能比为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
的普通类,则不是太难,但是好吧,你用手动内存管理打开了一大堆蠕虫,包括回收仍在使用的对象的风险。这些对象不能像上面的示例中那样像不可变对象一样共享。并且您可以使用查找可回收池化实例的操作以及在使用后显式放回实例的操作来替换对象分配 - 没有理由说这应该更快。