web-dev-qa-db-fra.com

Cartes imbriquées et clés combinées

dans le projet sur lequel je travaille actuellement, nous avions trois types de prix différents selon l'âge de l'utilisateur (adulte, enfant, etc ...). Nous avions donc sur la DB une table ressemblant à ceci:

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

Au début, nous n'avions que 4 types de prix différents, donc dans le code, nous avions quelque chose comme ceci:

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

Où les clés étaient le type de prix.

Récemment, ils ont ajouté une nouvelle règle commerciale qui ajoute 3 sous-types à chaque type de prix, nous avons donc maintenant quelque chose comme ceci:

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

Selon vous, laquelle des deux options suivantes est la meilleure et pourquoi?

Cartes imbriquées

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

où les clés sont le type et le sous-type de prix:

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

Clés combinées

La même carte qu'à l'origine:

Map<String, BigDecimal> prices;

Et concaténer les clés pour indexer les différents prix:

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

Les clés imbriquées et combinées ont leur place. bowmore donne un argument pro pour les clés composites et un argument con pour les cartes imbriquées. Permettez-moi de fournir l'opposition loyale:

Les clés de carte composites fonctionnent très bien lorsque vous recherchez un élément connu spécifique.

Les cartes imbriquées fonctionnent bien lorsque vous devez trouver rapidement toutes les variantes, types et sous-types de type A. Par exemple, choisir A (par rapport à B, C, ...) peut être la première étape d'un arbre de décision. Une fois que l'utilisateur ou l'algorithme ou quoi que ce soit choisit A, vous devez seulement connaître les sous-types de A, et B..Z ou B..ZZZZZ n'ont plus d'importance.

Vous avez maintenant affaire à une structure de recherche très étroite et efficace pour la sous-recherche. Si vous essayez de le faire avec des clés composites, vous finissez par faire une analyse complète de la table à la [ (key, value) for (key, value) in prices.items() if key.startswith('A') ]. Ce n'est pas une opération efficace, et sera lent si la carte est large.

Les cartes imbriquées fonctionnent également bien lorsque le nombre de niveaux de nidification peut augmenter. La structure du problème est déjà étendue de type à (type, subtype). Y a-t-il une chance que la prochaine révision nécessite (type, subtype, variation) ou (type, subtype, version)? Si tel est le cas, une approche de mappage imbriquée peut être proprement étendue. Il s'agit cependant d'un avantage stylistique de second ordre, en particulier par rapport à l'avantage de "recherche facile" ci-dessus.

8
Jonathan Eunice

Évitez les cartes imbriquées. Ils sont plus difficiles à mettre à l'échelle et conduisent à un code très verbeux et difficile à lire avec toutes les déclarations génériques imbriquées en cours.

Plus important encore, les cartes dans Java ont tendance à consommer beaucoup de mémoire. Remplir une carte avec encore plus de cartes ne fera qu'aggraver le problème de consommation de mémoire.

Enfin, une carte qui utilise des clés composites est plus facile à raisonner.

L'utilisation de clés composites vous facilitera la vie dans les cas les plus courants, mais certaines choses seront plus difficiles. Obtenir tous les prix pour un composant clé spécifique par exemple, mais vous êtes plus susceptible de demander ce résultat directement de la base de données plutôt que de le distiller de la carte.

6
bowmore

C'est moins à voir avec "quelle implémentation est la meilleure" et plus à voir avec "avec quelle abstraction dois-je travailler".

La clé composite et la carte des cartes ont toutes deux leurs forces et leurs faiblesses, qui résident toutes dans le domaine des performances (c'est-à-dire la vitesse/l'utilisation de la mémoire). Ils ne diffèrent pas dans leur fonctionnalité: ils prennent tous deux deux valeurs et renvoient la valeur précédemment "mise".

Comme ils sont fonctionnellement équivalents, la première chose à faire est de les résumer. Ne vous inquiétez pas de savoir lequel est le meilleur. Créez une interface DoubleKeyedMap avec toutes les méthodes dont vous avez besoin et utilisez-la dans votre code. Ensuite, écrivez l'implémentation que vous pouvez écrire le plus rapidement et passez à autre chose.

UNIQUEMENT une fois que vous avez écrit votre application et que vous avez constaté que votre implémentation de clé composite ne filtre pas très rapidement sur la première clé, ou que la carte des cartes prend trop de mémoire si vous allez l'optimiser.

L'optimisation prématurée est la racine de tout mal. Ne pas abstraire est pire.

1
Ben Seidel

Les deux options ne sont pas bonnes à mon avis. Dites-vous si la logique métier change à nouveau pour que vous ayez un autre sous-type?

Ce que je vous suggère de faire est le suivant:

  1. Utilisez une clé de substitution pour un appel de table Type cette table ressemblerait à Type(INT auto_inc id, VARCHAR(255) name, VARCHAR(255) desc, INT status, etc)
  2. Dans votre table Price, utilisez une clé étrangère vers la table Type ci-dessus pour qu'elle ressemble à Price(INT type_id, INT price)
  3. Assurez-vous de ne PAS prendre en charge la suppression d'un type. Vous marquez simplement un type comme inactive car les références pendantes feraient de la suppression un vrai casse-tête

Désormais, la logique utilisateur et la logique métier peuvent ajouter n'importe quelle combinaison de sous-types de type dans le schéma. Ce qu'ils devraient faire est simplement de créer une nouvelle ligne dans la table Type et de changer name et/ou desc dans l'ancienne table.

Dans votre cas, l'utilisateur renommerait le type A en type A_1, ajoutez un nouveau type appelé A_2 et définissez un nouveau prix pour A_2 comme 15

0
InformedA