web-dev-qa-db-ja.com

ネストされたマップと結合キー

私が現在取り組んでいるプロジェクトでは、ユーザーの年齢(大人、子供など)に応じて3種類の価格がありました。そのため、DBには次のようなテーブルがありました。

PRICES
type     Amount
A         20
B         15
C         ..
D         ..

最初は4種類の価格しかなかったので、コードには次のようなものがありました。

Map<String, BigDecimal> prices = new HashMap<String, BigDecimal>();

キーは価格タイプでした。

最近、彼らはすべての価格タイプに3つのサブタイプを追加する新しいビジネスルールを追加したので、次のようになります。

PRICES
type   subtype  Amount
A          1      20
A          2      15
A          3      ..
B          1      ..
B          2      ..
...        ..     ..

次の2つのオプションのどちらが優れていると思いますか?

ネストされたマップ

Map<String, Map<String, BigDecimal>> prices;

ここで、キーは価格タイプとサブタイプです。

prices.get(type).get(subtype);

結合キー

最初と同じマップ:

Map<String, BigDecimal> prices;

キーを連結して、さまざまな価格にインデックスを付けます。

prices.get(type+"_"+subtype);
8
mario595

ネストされたキーと結合されたキーの両方に場所があります。 bowmoreは、複合キーのpro引数と、ネストされたマップのcon引数を提供します。忠実な反対を提供させてください:

複合マップキーは、特定の既知のアイテムを検索する場合に最適です。

ネストされたマップは、タイプAのすべてのバリエーション、種類、およびサブタイプをすばやく検索する必要がある場合に適切に機能します。たとえば、A(対B、C、...)を選択することは、意思決定ツリーの最初のステップになる場合があります。ユーザーまたはアルゴリズムまたは何かがAを選択したら、Aのサブタイプについてのみ知る必要があり、B..ZまたはB..ZZZZZはもはや重要ではありません。

これで、サブサーチのための非常にタイトで効率的なルックアップ構造を扱っています。複合キーでそれを行おうとすると、最終的には[ (key, value) for (key, value) in prices.items() if key.startswith('A') ]でテーブル全体をスキャンすることになります。これは効率的な操作ではなくであり、マップが非常に大きい場合は遅くなります。

ネストされたマップは、ネストレベルの数が増える場合にもうまく機能します。問題の構造はすでにtypeから(type, subtype)に拡張されています。次のリビジョンで(type, subtype, variation)または(type, subtype, version)が必要になる可能性はありますか?その場合、ネストされたマッピングアプローチをきれいに拡張できます。ただし、これは特に上記の「簡単なサブサーチ」の利点と比較して、スタイル上の2次的な利点です。

8
Jonathan Eunice

マップのネストは避けてください。それらはスケーリングが難しく、コードが非常に冗長になり、ネストされたすべてのジェネリック宣言が続いていると読みにくくなります。

さらに重要なことに、Javaのマップは大量のメモリを消費する傾向があります。さらに多くのマップをマップに入力すると、メモリ消費の問題が悪化するだけです。

最後に、複合キーを使用するマップの方が推論しやすいです。

複合キーを使用すると、最も一般的なケースでは作業が楽になりますが、難しいことがあります。たとえば、特定の主要コンポーネントのすべての価格を取得しますが、その結果をマップから抽出するのではなく、データベースから直接クエリする可能性が高くなります。

6
bowmore

これは、「どの実装が最適か」ではなく、「どの抽象化で作業する必要があるか」に関係しています。

複合キーとマップのマップの両方に長所と短所があり、それらはすべてパフォーマンスのドメイン(つまり、速度/メモリ使用量)の範囲内にあります。これらは機能に違いはありません。どちらも2つの値を取り、以前の「プット」値を返します。

それらは機能的に同等なので、最初にすべきことはそれらを抽象化することです。どちらが良いか心配しないでください。必要なすべてのメソッドを含むDoubleKeyedMapインターフェースを作成し、それをコードで使用します。次に、最速で実行できる実装を記述します。

アプリケーションを作成し、複合キーの実装が最初のキーで非常に高速にフィルタリングしないこと、または最適化を行った場合にマップのマップが大量のメモリを使用していることがわかった場合にのみ。

時期尚早の最適化は、すべての悪の根源です。抽象化しないほうが悪い。

1
Ben Seidel

どちらのオプションも私の意見では良くありません。別のサブタイプを持つようにビジネスロジックが再び変更された場合はどうでしょうか。

私があなたがすることを提案するのは以下です:

  1. テーブル呼び出しに代理キーを使用するTypeこのテーブルはType(INT auto_inc id, VARCHAR(255) name, VARCHAR(255) desc, INT status, etc)のようになります
  2. Priceテーブルで、上記のTypeテーブルへの外部キーを使用すると、Price(INT type_id, INT price)のようになります。
  3. タイプの削除をサポートしていないことを確認してください。ぶら下がっている参照は削除を実際の頭痛の種にするため、タイプをinactiveとしてマークするだけです

これで、ユーザーロジックとビジネスロジックは、あらゆるタイプのサブタイプの組み合わせをスキーマに追加できます。彼らがする必要があるのは、Typeテーブルに新しい行を作成し、古いテーブルのnameまたはdescを変更することだけです。

あなたの場合、ユーザーはタイプAをタイプA_1に名前変更し、A_2という新しいタイプを追加して、A_2の新しい価格を15として設定します。

0
InformedA