第三场阴影场与属性访问器接口
這是“ 影子字段與屬性訪問器”界面的 第3輪 。 如果您是新手,但不確定要怎么做,請查看我以前的文章或關于開發JavaFX應用程序時節省內存的第一篇文章 。 作為Java開發人員,我主要關心的是在開發JavaFX域模型時在性能 , 內存使用和降低樣板代碼(易于使用API??)之間取得良好的平衡。 通常,應用程序框架提供模型視圖控制器(MVC)或表示模型模式以將UI代碼與域對象分開。 實際上,想到的是域模型類型對象應該易于創建或生成( IDE )。 在此博客條目中,您將看到第3輪的結果,其中包括兩個部分 。 第1部分是使用Marcel Heckel的想法實施的,第2部分是我最終根據性能 , 內存使用和易用性實現的一種實現。
- 免責聲明:使用任何代碼均需自擔風險。 這純粹是實驗性的,不應在生產中使用。 這是一個進展中的工作。
最新的代碼在這里—> [ PropertyAccessors接口 ]
第二輪回顧
盡管上一輪( 第2輪 )表明我的Property Accessors策略在內存消耗方面比標準(fat)特性對象策略稍微好一點,但是當創建2,000,000個具有本地類型的Employee類型類的對象時,它在性能方面仍然令人失望輸入對象 。 與Dirk的實現相比,我對第二輪實現的內存使用仍然不滿意。 如果您只關心我在第3輪中的最終成績,請跳至“ 結果”部分。
因為,可能會有新的回合,如果您決定使用它,或者假設Dirk決定在此處接受我的拉取請求,請轉到此處查看當前代碼。
在第2輪中,我使用了一個哈希映射查找,因為隨著O(1)時間復雜度(搜索)越來越多地添加了越來越多的字段,它的查找速度可能會非常慢。 有趣的是,Marcel Heckel提出了一種創建對象索引數組的簡單方法,該方法不僅可以節省更多內存,而且速度更快。 與鍵/值對查找相比,直接訪問字段絕對是必經之路。 盡管Marcel的代碼速度更快,但它仍然比Dirk的Shadow Fields代碼占用更多的內存。 額外的內存實際上是通過預分配一個數組來占用的,該數組將保存每個字段的值。 即使它們全為空 ,也會為每個員工對象創建數組本身。 我在這里實施了Marcel的策略(第23行)。 讓我們看一下索引字段數組策略的結果。
第1部分:使用索引字段數組
private final Object[] modelProperties =new Object[FIELDS.values().length];public Object[] getModelProperties(){return modelProperties;}測試:對象不使用屬性字段
下面顯示的是混合使用Marcel的索引數組思想和我使用枚舉類型指定屬性名稱以將字段表示為屬性字段的方式。
與所有字段均作為JavaFX屬性的標準(胖)對象相比,未使用JavaFX屬性的對象。 此實現對每個字段使用數組索引,并使用數組保存每個值。
在上方,您會注意到未選中該復選框,以指示不要在域對象上創建JavaFX 屬性 ( 不使用xxxxProperty()方法 )。 您會注意到,與第二回合的代碼相比,性能得到了顯著提高,并且內存使用量也減少了。 在上圖中, Property Accessor界面比Shadow Fields的模式實現多了16MB 。 在瘦對象??性能和內存使用的第1部分中, Shadow Fields是明顯的贏家。 但是,Shadow Fields仍然不太干凈。 還要注意的另一件事是,對于200萬個對象,Property Accessors接口僅不到14毫秒! 正如我們將在第2部分稍后將帶回專用實例變量作為字段所看到的那樣 ,Property Accessors界面將真正隨著內存使用而發光。
測試:對象使用屬性字段
以下是對象的所有字段都使用JavaFX屬性時的結果。
使用JavaFX屬性的對象與標準(胖)對象相比,所有字段均作為Javafx屬性。 此實現對每個字段使用數組索引,并使用數組保存每個值。
在這里,您會注意到200萬個對象的Accessor列(Property Accessors接口)在916毫秒內執行,使用了576 MB的內存。 在這種情況下,就544 MB的內存空間而言,標準(胖)對象是獲勝者。 到目前為止,Shadow Fields在每一輪比賽中都表現出色。
Marcel的代碼示例 (在注釋部分)的一個小細節是,在創建新的屬性對象實例時,它沒有考慮屬性的字符串名稱 。 例如,以下語句顯示變量totalProperty ,其屬性名為“ total ”,該屬性與totalProperty()方法匹配。 在更改過程中觸發屬性名稱對于讀取代碼,測試和工具很重要。
屬性totalProperty = new SimpleIntegerProperty(this,“ total”,new Integer(5));
為了同時擁有一個命名字段和一個類似于Marcel的想法的索引,我只創建了一個枚舉來聲明每個字段的屬性。 這些枚舉在Employee類上創建。
// Part 1 implementation public class EmployeePropertyAccessor implements PropertyAccessors{public enum FIELDS {name,powers,supervisor,minions}private final Object[] modelProperties =new Object[FIELDS.values().length];public Object[] getModelProperties(){return modelProperties;}// The rest of the code...在上面,您會注意到將如何基于已定義字段(枚舉FIELDS)的數量創建模型Properties數組。 我使用FIELDS.value()。length定義數組的大小。 另外, PropertyAccessors接口( 第1部分的實現 )強制開發人員實現getModelProperties()方法。 在這里,我只是返回對對象數組的modelProperties引用。 “ 必須 ”實現一個數組和一個getModelProperties()方法并不是一件很愉快的事情 。
在本文的第2部分中,我以不同的方式實現了一些事情,其中??開發人員沒有被迫實現modelProperties數組和getModelProperties()方法。 我將解決此問題,使代碼看起來更簡潔,性能更好(API觀點的用戶)。
第2部分:重新引入實例變量
在第2部分中,我將把私有實例變量重新添加到Employee類( EmployeePropertyAccessor )中,以保存字段值,而不是像第1部分中那樣包含數組。我的想法是使字段變量與指向本機對象的任一點互斥類型或JavaFX屬性,從而與“影子字段”模式代碼相比節省了內存。 由于“ 陰影字段”代碼使用兩個變量來表示字段值,因此它將具有一個額外的引用,當對象使用屬性時,該引用將不可避免地增加其內存。 正如您在下面看到的那樣,該代碼看起來與第1部分相似,但也將具有一個靜態塊以在類中注冊屬性字段。 這很重要,因為您可能不希望某些實例變量作為JavaFX屬性參與。
// Part 2 implementation public class EmployeePropertyAccessor implements PropertyAccessors {private Object name;private Object powers;private Object supervisor;private Object minions;enum FIELDS {name,powers,supervisor,minions}static {// register fields one time.// (Warning: enum's ordinal value is reassigned an index number)registerFields(EmployeePropertyAccessor.class, FIELDS.values());}public EmployeePropertyAccessor(String name, String powers) {setName(name);setPowers(powers);}public final String getName() {return getValue(FIELDS.name, "");}public final void setName(String name) {setValue(FIELDS.name, name);}public final StringProperty nameProperty() {return refProperty(FIELDS.name, SimpleStringProperty.class, String.class);}// The rest of the code...上面的代碼清單在調用registerFields()方法時做了一些有趣的魔術 。 使用反射重新分配 FIELDS枚舉的序數值,從而為每個枚舉賦予一個新的id作為數組的索引。 這提供了不可變的枚舉,同時還包含要通過索引快速訪問的每個字段的唯一標識符。 由于枚舉用于表示要用作屬性的字段,因此序數值在其他上下文中沒有意義。 這意味著:誰在乎,是否在這些聲明的枚舉上重新分配了序數值? 它們僅用于“ 注冊字段 ”。
測試:對象不使用屬性字段[NEW]
下面顯示的是使用Property Accessors接口API的新實現的測試結果。 下面的測試顯示了與標準胖對象相比,何時不使用屬性字段。
將標準對象(使用屬性的所有字段)與使用本機對象的對象進行比較的測試。 現在,Property Accessors接口API使用實例變量作為字段,而不是對數組的引用。
正如您在上面看到的那樣,Property Accessors接口的新實現顯然是內存使用率和易用性的贏家。 性能比第1部分的實現稍慢,但是節省內存是值得的。 您會注意到,“影子字段”的內存使用量比“屬性訪問器”的使用量多16 MB。
測試:對象使用屬性字段[NEW]
下面顯示的是使用Property Accessors接口API的新實現的測試結果。 下面的測試顯示與標準脂肪對象相比,何時使用屬性字段。 (在“開始”按鈕下方選中了該復選框)
第三回合的結果
以下是我根據表格中的結果匯總在一起的條形圖。 我覺得人們喜歡看圖表而不是表格,單元格和文本。
對象不使用JavaFX屬性時的性能測試結果。 較小的數字(以毫秒為單位)更好。
對象使用JavaFX屬性時的性能測試結果。 較小的數字(以毫秒為單位)更好。
對象不使用JavaFX屬性時的內存使用情況測試結果。 較小的數字(以兆字節為單位)更好。
對象使用JavaFX屬性時的內存使用情況測試結果。 較小的數字(以兆字節為單位)更好。
結論
根據結果??,我的目標是明確實現的 (IMHO),我最初希望代碼在對象可能使用或不使用JavaFX屬性時都易于閱讀并且易于實現( 當字段不使用JavaFX時還可以節省內存)屬性[本機類型] )。 盡管在所有測試運行中都贏得了性能方面的Shadow Fields的認可,但Property Accessors界面并沒有落后。 當不使用屬性時,在創建200萬條記錄時,Property Accessors界面僅比標準對象策略短5毫秒。
對于創建200萬個對象的內存使用情況,以及當策略未使用屬性作為字段時, Property Accessors界面顯然是贏家,與Shadow Fields模式相比至少節省16MB ,與Shadow Fields模式相比節省240MB。標準屬性代碼。 最后,但并非最不重要的是,當對象將屬性用作字段時,Property Accessors接口與有關內存消耗的Standard對象策略相關聯。 Shadow Fields策略使用的內存至少比其他策略多20MB。
盡管在使用或不使用所有字段作為200萬個對象的屬性時,Properties Accessors接口會稍慢一些(以毫秒為單位的微小差異),但我相信該API可以在任何JavaFX應用程序大小上使用,以簡化域的開發模型和對象。 我鼓勵其他人在決定使用API??之前先測試代碼。 請注意,該代碼不適合生產,并且處于試驗階段。 這是一項正在進行中的工作,因此,在我開始吃自己的狗食(工作時)之前,我真的不建議您使用Property Accessors API。 我將在下面重申免責聲明:
- 免責聲明:使用任何代碼均需自擔風險。 這純粹是實驗性的,不應在生產中使用。 這是一個進展中的工作。
隨時發表評論和訂閱。 祝您編程愉快!
翻譯自: https://www.javacodegeeks.com/2016/04/shadow-fields-vs-property-accessors-interface-round-3.html
總結
以上是生活随笔為你收集整理的第三场阴影场与属性访问器接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工行跨行转账多久到账?
- 下一篇: 如何在几分钟内安装Red Hat Con