簡體   English   中英

HashMap替代內存高效的數據存儲

[英]HashMap alternatives for memory-efficient data storage

我目前有一個電子表格類型程序,它將數據保存在HashMaps的ArrayList中。 當我告訴你這沒有被證明是理想的時候,你無疑會感到震驚。 開銷似乎比數據本身多5倍的內存。

這個問題詢問有效的集合庫,答案是使用Google Collections。 我的跟進是“ 哪一部分? 我一直在閱讀文檔,但不覺得它非常好地了解哪些類適合這個。 (我也對其他圖書館或建議開放)。

所以我正在尋找能夠以最小的內存開銷存儲密集的電子表格類型數據的東西。

  • 我的列當前由Field對象引用,行由它們的索引引用,值是Objects,幾乎總是字符串
  • 有些列會有很多重復的值
  • 主要操作是根據某些字段的值更新或刪除記錄,以及添加/刪除/組合列

我知道H2和Derby等選項,但在這種情況下我不打算使用嵌入式數據庫。

編輯 :如果你建議圖書館,我也很感激你,如果你能指出我在這里適用的特定的一兩節課。 雖然Sun的文檔通常包含哪些操作是O(1)的信息,哪些是O(N)等,但我在第三方庫中沒有看到太多,也沒有真正描述哪些類最適合什么類。

有些列會有很多重復的值

無論您為收藏品選擇何種解決方案,我都會立即向我建議使用FlyWeight模式

Trove集合應特別關注占用的空間(我認為如果你堅持原始類型,它們也有定制的數據結構)。看看這里

否則你可以試試Apache系列 ..只需做你的基准測試!

在任何情況下,如果你有很多參考相同的元素嘗試設計一些適合的模式(如flyweight

所以我假設您有一個Map<ColumnName,Column>Map<ColumnName,Column> ,其中列實際上類似於ArrayList<Object>

一些可能性 -

  • 你完全確定記憶是個問題嗎? 如果你只是普遍擔心尺寸,那么值得確認這在運行程序中確實會成為一個問題。 它需要大量的行和映射才能填滿JVM。

  • 您可以使用集合中不同類型的地圖測試數據集。 根據您的數據,您還可以使用可能有用的預設尺寸/載荷系數組合初始化地圖。 我過去曾經搞過這個問題,如果你幸運的話,你可能會減少30%的記憶力。

  • 如何將數據存儲在單個類似矩陣的數據結構(現有的庫實現或類似於列表列表的包裝器之類)中,使用單個映射將列鍵映射到矩陣列?

假設您的所有行都具有大多數相同的列,您可以只為每一行使用一個數組,並使用Map <ColumnKey,Integer>來查找哪些列引用哪個單元格。 這樣,每個單元只有4-8個字節的開銷。

如果經常重復使用字符串,則可以使用字符串池來減少字符串的重復。 其他不可變類型的對象池可用於減少消耗的內存。

編輯:您可以將數據結構化為基於行或基於列。 如果基於行(每行一個單元格數組)添加/刪除行只是刪除此行的問題。 如果基於列,則每列可以有一個數組。 這可以使處理原始類型更有效。 也就是說,你可以有一個是int []的列,另一個是double [],它更常見於整個列具有相同的數據類型,而不是整行的數據類型相同。

但是,無論采用哪種方式來構造數據,都可以選擇進行行或列修改,並執行其他類型的添加/刪除將導致重建整個數據集。

(我做的事情是有基於行的數據並添加列到最后,假設一行不夠長,列有一個默認值,這可以避免在添加列時重建。而不是刪除列,我有一種忽視它的方法)

Guava確實包含Table接口和基於哈希的實現。 似乎很適合您的問題。 請注意,這仍然標記為測試版。

將其數據保存在HashMaps的ArrayList中
嗯,這部分對我來說似乎非常低效。 空HashMap已經分配了16 * size of a pointer字節(16代表默認初始容量),以及散列對象的一些變量(14 + psize)。 如果你有很多稀疏的行,這可能是一個大問題。

一種選擇是使用具有復合鍵的單個大散列(組合行和列)。 雖然,這不會使整行操作非常有效。

此外,由於您未提及添加單元格的操作,因此可以僅使用必要的內部存儲( initialCapacity參數)創建哈希。

我不太了解谷歌收藏,所以無法幫助那里。 此外,如果您發現任何有用的優化,請在此處發布! 知道會很有趣。

我一直在嘗試使用Colt項目中的SparseObjectMatrix2D 我的數據非常密集,但他們的Matrix類並沒有真正提供任何擴大它們的方法,因此我選擇了一個稀疏矩陣設置為最大尺寸。

對於相同的數據,它似乎使用大約10%的內存並加載大約15%,並提供一些巧妙的操作方法。 仍然對其他選項感興趣。

Chronicle Map每個條目的開銷可能少於20個字節(參見測試證明這一點)。 為了進行比較,java.util.HashMap的開銷從37-42字節到-XX:+UseCompressedOops到58-69字節不帶壓縮oops( 引用 )。

此外,Chronicle Map在堆外存儲鍵和值,因此它不存儲Object頭,這些頭部不會被視為HashMap的開銷。 Chronicle Map與Chronicle-Values 集成Chronicle-Values是一個用於生成接口flyweight實現的庫, Brian Agnew在另一個答案中提出了這種模式。

為什么不嘗試使用像EHCache這樣的緩存實現。 當我遇到同樣的情況時,這對我來說非常有效。
您可以將您的集合存儲在EHcache實現中。 有如下配置:

Maximum bytes to be used from Local heap.

一旦應用程序使用的字節溢出了緩存中配置的字節,則緩存實現會負責將數據寫入磁盤。 您還可以使用最近最少使用的算法配置將對象寫入磁盤的時間量。 使用這種類型的緩存實現,您可以確保避免任何內存不足錯誤。 它只會在很小程度上增加應用程序的IO操作。
這只是配置的鳥瞰圖。 有許多配置可以優化您的要求。

根據您的描述,似乎您不想使用HashMaps的ArrayList而是需要ArrayList的(鏈接)HashMap (每個ArrayList都是一列)。

我將從field-name添加到map-number的雙映射,以及一些從不拋出IndexOutOfBoundsException聰明的getter / setter。

您還可以使用ArrayList<ArrayList<Object>> (基本上是鋸齒狀的dinamically增長矩陣)並保持映射到外部的字段(列)名稱。

有些列會有很多重復的值

我懷疑這很重要,特別是如果它們是字符串,(它們是內化的),你的收藏將存儲對它們的引用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM