2016-07-29 60 views
1

我就可得知用於捕獲lambda表達式,存在需要的對象分配(它是Object[]或一些abc$Lambda$xyz類型)。無論如何可以自定義這個過程嗎?比方說,我有這樣的代碼:普爾捕獲的lambda

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

,我會需要對所有可能的類型lambda表達式我要使用特定的簽名這樣的持有人。

也許我已經通過提供函數使示例複雜一點 - 但我想要捕捉一個事實,即在調用Supplier.get()時可能會對捕獲的參數應用額外的計算。

並請忽略的事實是int值裝箱時可產生分配。

+0

你的意思是像'supplier =() - > pool.getInteger();'? – bradimus

+0

@bradimus對不起,我不明白這個問題。目的是得到一個合併對象,因爲它的業務邏輯返回5.池不能瞭解這個業務邏輯。 –

+0

如果函數也是不捕獲的,這個持有者實現應該可以幫助您防止創建對象。我非常好奇你在哪裏必須優化這個部分,你能否提供一下它將被使用的地方? –

回答

1

爲「池捕捉lambda表達式」是用詞不當。 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一個普通的類,但是好了,你打開一個整體,手動內存管理的蠕蟲,包括回收仍在使用中的對象的風險。這些對象不能像上面例子中的不可變對象那樣共享。你可以用找到一個可回收的池化實例的動作,以及在使用後明確放回一個實例的動作來替換對象分配 - 沒有理由爲什麼這會更快。

+0

用'SupplierCache.getSupplier(x)'代替'() - > x''是我想要避免的。 –

+0

好吧,如果你想保留lambda表達式的源代碼形式,就不會有解釋的內置解決方案,因爲結果可疑。但是,在編譯的類文件上執行它是可能的。一般來說,JRE是負責的,所以如果你想在不改變JRE的情況下注入不同的行爲,你必須使用字節碼操作/ Instrumentation來將引導方法重定向到具有該緩存功能的替代實現。 – Holger

+0

@Holger關於LRU緩存的好方法..絕對是一個加號! – Eugene