Mapas anidados versus teclas combinadas

8

En el proyecto en el que estoy trabajando actualmente teníamos tres tipos diferentes de precios según la edad del usuario (adulto, niño, etc.). Así que teníamos en la base de datos una tabla con este aspecto:

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

Al principio solo teníamos 4 tipos diferentes de precios, así que en el código, teníamos algo como esto:

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

Donde las claves eran el tipo de precio.

Recientemente, agregaron una nueva regla comercial que agrega 3 subtipos a cada tipo de precio, por lo que ahora tenemos algo como esto:

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

¿Cuál de las siguientes dos opciones crees que es mejor y por qué?

Mapas anidados

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

donde las claves son el tipo de precio y el subtipo:

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

Llaves combinadas

El mismo mapa que originalmente:

Map<String, BigDecimal> prices;

Y concatene las claves para indexar los diferentes precios:

prices.get(type+"_"+subtype);
mario595
fuente
Esta es más una pregunta de diseño, y no hay código suficiente para la revisión de código.
200_success
Una pequeña nota: su clave combinada propuesta puede ocasionar problemas en el futuro, por ejemplo, necesitar una '_' en un tipo de precio. Considere en cambio tener una class PriceKey{ PriceType type; PriceSubtype subtype; }llave. Esto se puede extender fácilmente aún más
Caleth

Respuestas:

8

Ambas teclas anidadas y combinadas tienen sus lugares. bowmore da un argumento pro para claves compuestas, y un argumento en contra para mapas anidados. Permítanme proporcionar la oposición leal:

Las teclas de mapa compuestas funcionan muy bien cuando busca un elemento conocido específico.

Los mapas anidados funcionan bien cuando necesita encontrar rápidamente todas las variaciones, tipos y subtipos de tipo A. Por ejemplo, elegir A (vs. B, C, ...) podría ser el primer paso en un árbol de decisión. Una vez que el usuario o el algoritmo o lo que sea que elija A, solo necesita saber acerca de los subtipos de A, y B..Z o B..ZZZZZ ya no importan.

Ahora se trata de una estructura de búsqueda muy ajustada y eficiente para la búsqueda secundaria. Si intentas hacer eso con teclas compuestas, terminas haciendo un escaneo completo de la tabla a la [ (key, value) for (key, value) in prices.items() if key.startswith('A') ]. Esa no es una operación eficiente, y será lenta si el mapa es bastante grande.

Los mapas anidados también funcionan bien cuando puede aumentar el número de niveles de anidación. La estructura del problema ya se extendió de typea (type, subtype). ¿Hay alguna posibilidad de que la próxima revolución necesite (type, subtype, variation)o (type, subtype, version)? Si es así, un enfoque de mapeo anidado se puede extender limpiamente. Esto, sin embargo, es una ventaja estilística de segundo orden, especialmente en comparación con la ventaja de "búsqueda fácil" anterior.

Jonathan Eunice
fuente
A decir verdad, la búsqueda incremental de claves compuestas puede evitar una 'exploración de tabla' mediante el uso de SortedMap.
bowmore
Puede mejorar las características de búsqueda de claves compuestas ordenando el mapa. Pero no puede evitar todo el escaneo de esa manera. Encontrar la clave "RXR_1_a" en una tabla o mapeo ordenado de 10,000 claves, por ejemplo, todavía requiere encontrar el índice de la tabla donde "RXR_ *" comienza y termina. Incluso con la búsqueda binaria, este no es el tiempo cero. También requiere las mismas key.startswith('RXR')pruebas que mencioné. Los mapas anidados son siempre operaciones O (1), y no requieren la sobrecarga de ordenar previamente el mapa, o comparaciones de cadenas.
Jonathan Eunice
Así es, acabo de decir que podría evitar una operación de estilo 'exploración de tabla'. En mi respuesta también señalo que para este tipo de búsqueda es más fácil consultar el DB directamente. Ambos enfoques tendrán problemas con la búsqueda de una clave parcial que no comience en el primer campo, y una base de datos puede aliviar esto con índices adicionales.
bowmore
Un SortedMap puede evitar una búsqueda lineal completa, pero no una búsqueda de alto costo. No veo cómo ayudar a empeñar la operación en una base de datos; eso puede simplificar el código del cliente, tal vez, pero a costa de solicitudes externas lentas y estructuras de datos adicionales (los índices necesarios para que la operación sea incluso razonablemente eficiente). Una estructura anidada tiene algunos inconvenientes, claro, pero es autónoma y altamente eficiente, con o sin soporte de base de datos de respaldo.
Jonathan Eunice
¿Cómo es una búsqueda binaria una búsqueda de alto costo?
bowmore
6

Evita los mapas anidados. Son más difíciles de escalar y conducen a un código que es muy detallado y difícil de leer con todas las declaraciones genéricas anidadas.

Más importante aún, los mapas en Java tienden a consumir mucha memoria. Llenar un mapa con aún más mapas solo agravará el problema del consumo de memoria.

Por último, un Mapa que usa claves compuestas es más fácil de razonar.

El uso de teclas compuestas facilitará su vida en los casos más típicos, aunque algunas cosas serán más difíciles. Obtener todos los precios de un componente clave específico, por ejemplo, pero es más probable que consulte ese resultado directamente desde la base de datos en lugar de destilarlo desde el Mapa.

Bowmore
fuente
Estoy de acuerdo con su declaración, sin embargo, a veces los mapas anidados son inevitables, y en ese escenario no he encontrado una buena manera de manipularlos en Java. Siempre que necesitemos leer / manipular algo genérico en Java, particularmente archivos JSON, necesitará mapas anidados, a menos que desee codificar la estructura de su archivo en su aplicación.
froginvasion
1

Esto tiene menos que ver con "qué implementación es la mejor" y más con "con qué abstracción debería estar trabajando".

Tanto la clave compuesta como el mapa de mapas tienen sus puntos fuertes y sus puntos débiles, todos los cuales residen en el dominio del rendimiento (es decir, velocidad / uso de memoria). No difieren en su funcionalidad: ambos toman dos valores y devuelven el valor "puesto" previamente.

Como son funcionalmente equivalentes, lo primero que debe hacer es hacer un resumen sobre ellos. No te preocupes por cuál es mejor. Cree una interfaz DoubleKeyedMap con todos los métodos que necesita y úsela en su código. Luego escriba cualquier implementación que pueda escribir más rápido y simplemente continúe.

SOLO una vez que haya escrito su aplicación y haya descubierto que su implementación de clave compuesta no se filtra en la primera clave muy rápido, o que el mapa de mapas está tomando demasiada memoria en caso de que vaya y optimice.

La optimización prematura es la raíz de todo mal. No abstraer es peor.

Ben Seidel
fuente
Bien dicho. ¡No abstraer es peor! Me encanta eso
Zack Bartel
0

Ambas opciones no son buenas en mi opinión. ¿Qué sucede si la lógica de negocios cambia nuevamente para que tenga otro subtipo?

Lo que te sugiero que hagas es lo siguiente:

  1. Utilice una clave sustituta para una tabla, llame a Typeesta tabla como se veríaType(INT auto_inc id, VARCHAR(255) name, VARCHAR(255) desc, INT status, etc)
  2. En su Pricetabla, use la clave externa de la Typetabla de arriba para que se vea comoPrice(INT type_id, INT price)
  3. Asegúrese de NO admitir la eliminación de un tipo. Simplemente marca un tipo inactiveporque las referencias colgantes harían que la eliminación sea un verdadero dolor de cabeza

Ahora, la lógica de usuario y de negocio puede agregar cualquier combinación de subtipo de tipo al esquema. Lo que tendrían que hacer es simplemente crear una nueva fila en la Typetabla y cambiar namey / o descen la tabla anterior.

En su caso, el usuario cambiaría el nombre de tipo Apor tipo A_1, agregaría un nuevo tipo llamado A_2y establecería un nuevo precio A_2como15

InformadoA
fuente