web-dev-qa-db-ja.com

メモリ効率の高いデータストレージのためのHashMapの代替案

現在、データをHashMapのArrayListに保持するスプレッドシートタイプのプログラムがあります。これが理想的とは言えないことを私が言ったとき、あなたはきっとショックを受けるでしょう。オーバーヘッドは、データ自体よりも5倍多くのメモリを使用するようです。

この質問 は効率的なコレクションライブラリについて尋ね、答えはGoogleコレクションを使用することでした。 私のフォローアップは、「どの部分ですか?です。私はドキュメントを読んでいますが、それがどのクラスがこれに適しているかを非常によく理解しているとは思わないでください。 (私は他のライブラリや提案にもオープンです)。

そこで、最小限のメモリオーバーヘッドで高密度のスプレッドシートタイプのデータを保存できるものを探しています。

  • 私の列は現在Fieldオブジェクトによって参照され、行はインデックスによって参照され、値はオブジェクトであり、ほとんどの場合文字列です
  • 一部の列には多くの繰り返し値があります
  • 主な操作は、特定のフィールドの値に基づいてレコードを更新または削除し、列を追加/削除/結合することです

H2やDerbyなどのオプションは知っていますが、この場合は組み込みデータベースを使用するつもりはありません。

[〜#〜] edit [〜#〜]:ライブラリを提案している場合は、ここで適用されるそれらの特定のクラスまたは2つ。 Sunのドキュメントには通常、O(1)、O(N)などの操作に関する情報が含まれていますが、サードパーティのライブラリではその多くは見られません。 。

27
Brad Mace

したがって、Map<ColumnName,Column>のマップがあり、実際には列がArrayList<Object>のようなものであると想定しています。

いくつかの可能性-

  • メモリが問題であることを完全に確信していますか?一般的にサイズについて心配しているだけなら、これが実行中のプログラムで本当に問題になることを確認する価値があります。これは、JVMを埋めるために非常に多くの行とマップを必要とします。

  • コレクション内のさまざまなタイプのマップを使用してデータセットをテストできます。データに応じて、事前設定されたサイズ/負荷係数の組み合わせを使用してマップを初期化することもできます。私は過去にこれをいじりましたが、運が良ければ、メモリが30%削減される可能性があります。

  • 列キーをマトリックス列にマップする単一のマップを使用して、データを単一のマトリックスのようなデータ構造(既存のライブラリ実装またはリストのリストのラッパーのようなもの)に保存するのはどうですか?

4
Steve B.

一部の列には多くの繰り返し値があります

コレクションに選択したソリューションに関係なく、 FlyWeightパターン の可能な使用法をすぐに提案します。

12
Brian Agnew

Troveコレクションは、占有されているスペースに特に注意する必要があります(プリミティブ型に固執する場合は、データ構造も調整されていると思います)。 ここ

それ以外の場合は、 Apacheコレクション ..で試すことができます。ベンチマークを実行してください!

いずれにせよ、同じ要素への参照がたくさんある場合は、適切なパターンを設計してみてください( flyweight など)。

5
Jack

すべての行にほとんど同じ列があると仮定すると、各行の配列とMap <ColumnKey、Integer>を使用して、どの列がどのセルを参照しているかを検索できます。この方法では、セルごとに4〜8バイトのオーバーヘッドしかありません。

文字列が頻繁に繰り返される場合は、文字列プールを使用して文字列の重複を減らすことができます。他の不変タイプのオブジェクトプールは、消費されるメモリを減らすのに役立ちます。

編集:行ベースまたは列ベースのいずれかとしてデータを構造化できます。行ベース(行ごとに1つのセルの配列)の場合、行の追加/削除は、この行を削除するだけです。列に基づいている場合は、列ごとに配列を持つことができます。これにより、プリミティブ型の処理がはるかに効率的になります。つまり、int []である列とdouble []である別の列を持つことができます。これは、行全体で同じデータ型を持つのではなく、列全体で同じデータ型を持つほうがはるかに一般的です。

ただし、どちらの方法でデータを構造化しても、行または列の変更に最適化され、他のタイプの追加/削除を実行すると、データセット全体が再構築されます。

(私がすることは、行ベースのデータを持ち、列を最後に追加することです。行が十分に長くない場合、列にはデフォルト値があるため、列を追加するときに再構築が回避されます。列を削除するのではなく、それを無視する手段)

3
Peter Lawrey

Guavaには Table インターフェイスとハッシュベースの実装が含まれています。あなたの問題に自然に合っているようです。これはまだベータ版としてマークされていることに注意してください。

2
whiskeysierra

クロニクルマップ は、エントリあたり20バイト未満のオーバーヘッドが発生する可能性があります( a test を参照)。比較のために、Java.util.HashMapのオーバーヘッドは、-XX:+UseCompressedOopsを使用した場合の37〜42バイトから、圧縮oopを使用しない場合の58〜69バイトまで変化します( 参照 )。

さらに、Chronicle Mapはキーと値をオフヒープで格納するため、上記のHashMapのオーバーヘッドとして考慮されていないオブジェクトヘッダーは格納しません。クロニクルマップ integrates with Chronicle-Values 、インターフェイスのフライウェイト実装を生成するためのライブラリ、パターン Brian Agnewが推奨 別の回答。

1
leventov

Colt プロジェクトのSparseObjectMatrix2Dを使用して実験しています。私のデータはかなり密集していますが、それらのMatrixクラスは実際にそれらを拡大する方法を提供しないため、最大サイズに設定されたスパース行列を使用しました。

同じデータに対して、メモリの使用量を約10%削減し、ロードを約15%高速化するだけでなく、巧妙な操作方法も提供します。まだ他のオプションにも興味があります。

1
Brad Mace

HashMapのArrayListにデータを保持します
まあ、この部分は私にはひどく非効率的です。空のHashMapはすでに16 * size of a pointerバイト(16はデフォルトの初期容量を表します)に加えて、ハッシュオブジェクトのいくつかの変数(14 + psize)を割り当てます。多数のまばらな行がある場合、これは大きな問題になる可能性があります。

1つのオプションは、複合キー(行と列を組み合わせる)で単一の大きなハッシュを使用することです。ただし、これでは行全体の操作はそれほど効果的ではありません。

また、セルを追加する操作については触れていないので、必要な内部ストレージ(initialCapacityパラメータ)のみでハッシュを作成できます。

私はグーグルコレクションについてあまり知らないので、そこには助けられない。また、有用な最適化を見つけた場合は、ここに投稿してください!知っておくと面白いでしょう。

1
Nikita Rybak

あなたの説明から、HashMapのArrayListの代わりに(Linked)HashMap of ArrayListが必要なようです(各ArrayListは列になります)。

フィールド名から列番号への二重マップと、IndexOutOfBoundsExceptionをスローしない賢いゲッター/セッターを追加します。

ArrayList<ArrayList<Object>>(基本的にギザギザの動的に成長する行列)を使用して、フィールド(列)名へのマッピングを外部に保持することもできます。

一部の列には多くの繰り返し値があります

特にこれらが文字列である場合(それらが内部化されている場合)、これが問題になることは疑わしく、コレクションはそれらへの参照を格納します。

0
leonbloy

EHCache のようなキャッシュ実装を使用してみませんか。これは、私が同じ状況に遭遇したときに非常に効果的であることがわかりました。
EHcache実装内にコレクションを格納するだけで済みます。次のような構成があります。

Maximum bytes to be used from Local heap.

アプリケーションで使用されるバイトがキャッシュで構成されたバイトでオーバーフローすると、キャッシュの実装がディスクへのデータの書き込みを処理します。また、最低使用頻度アルゴリズムを使用してオブジェクトがディスクに書き込まれるまでの時間を設定することもできます。このタイプのキャッシュ実装を使用すると、メモリ不足エラーを確実に回避できます。アプリケーションのIO操作を少しだけ増やすだけです。
これは構成の鳥瞰図です。要件を最適化するための多くの構成があります。

0
NiranjanBhat