no projeto em que estou trabalhando, tivemos três tipos diferentes de preços, dependendo da idade do usuário (adulto, criança, etc.). Então, tivemos no banco de dados uma tabela parecida com esta:
PRICES type Amount A 20 B 15 C .. D ..
No começo, tínhamos apenas quatro tipos diferentes de preços; portanto, no código, tínhamos algo parecido com isto:
Map<String, BigDecimal> prices = new HashMap<String, BigDecimal>();
Onde as chaves eram o tipo de preço.
Recentemente, eles adicionaram uma nova regra de negócios que adiciona três subtipos a cada tipo de preço; agora, temos algo parecido com isto:
PRICES type subtype Amount A 1 20 A 2 15 A 3 .. B 1 .. B 2 .. ... .. ..
Qual das duas opções a seguir você acha que é melhor e por quê?
Mapas aninhados
Map<String, Map<String, BigDecimal>> prices;
onde as chaves são o tipo e subtipo de preço:
prices.get(type).get(subtype);
Chaves combinadas
O mesmo mapa que originalmente:
Map<String, BigDecimal> prices;
E concatene as chaves para indexar os diferentes preços:
prices.get(type+"_"+subtype);
java
data-structures
mario595
fonte
fonte
class PriceKey{ PriceType type; PriceSubtype subtype; }
chave. Este pode então ser facilmente prorrogadoRespostas:
As chaves aninhadas e combinadas têm seus lugares. bowmore fornece um argumento pro para chaves compostas e um argumento contra para mapas aninhados. Deixe-me fornecer a oposição leal:
As teclas de mapa composto funcionam muito bem quando você procura um item conhecido específico.
Os mapas aninhados funcionam bem quando você precisa encontrar rapidamente todas as variações, tipos e subtipos do tipo A. Por exemplo, escolher A (vs. B, C, ...) pode ser o primeiro passo em uma árvore de decisão. Depois que o usuário ou algoritmo ou o que quer que escolher A, você precisará conhecer apenas os subtipos de A e B..Z ou B..ZZZZZ não importam mais.
Agora você está lidando com uma estrutura de pesquisa muito rígida e eficiente para a sub-pesquisa. Se você tentar fazer isso com chaves compostas, acabará fazendo uma verificação completa da tabela a la
[ (key, value) for (key, value) in prices.items() if key.startswith('A') ]
. Essa não é uma operação eficiente e será lenta se o mapa for grande.Mapas aninhados também funcionam bem quando o número de níveis de aninhamento pode aumentar. A estrutura do problema já foi estendida de
type
para(type, subtype)
. Existe alguma chance de a próxima rev precisar(type, subtype, variation)
ou(type, subtype, version)
? Nesse caso, uma abordagem de mapeamento aninhado pode ser estendida de maneira limpa. Essa, no entanto, é uma vantagem estilística de segunda ordem, especialmente se comparada à vantagem da "fácil sub-pesquisa" acima.fonte
key.startswith('RXR')
testes que eu mencionei. Os mapas aninhados são sempre operações O (1) e não exigem a sobrecarga de pré-classificação do mapa ou comparações de cadeias.Evite mapas aninhados. Eles são mais difíceis de dimensionar e levam a códigos muito detalhados e difíceis de ler com todas as declarações genéricas aninhadas em andamento.
Mais importante, os mapas em Java tendem a consumir muita memória. Preencher um mapa com ainda mais mapas agravará o problema de consumo de memória.
Por fim, é mais fácil pensar em um mapa que usa chaves compostas.
O uso de chaves compostas facilitará sua vida nos casos mais comuns, mas algumas coisas serão mais difíceis. Obter todos os preços de um componente-chave específico, por exemplo, mas é mais provável que você consulte esse resultado diretamente do banco de dados, em vez de destilá-lo do Mapa.
fonte
Isso tem menos a ver com "qual implementação é melhor" e mais com "com qual abstração devo trabalhar".
Tanto a chave composta quanto o mapa de mapas têm seus pontos fortes e fracos, todos residindo no domínio do desempenho (ou seja, velocidade / uso de memória). Eles não diferem em sua funcionalidade: ambos pegam dois valores e retornam o valor "put" anteriormente.
Como eles são funcionalmente equivalentes, a primeira coisa que você deve fazer é abstrair sobre eles. Não se preocupe com qual é o melhor. Crie uma interface DoubleKeyedMap com todos os métodos necessários e use-a no seu código. Em seguida, escreva qualquer implementação que puder escrever mais rapidamente e siga em frente.
SOMENTE depois de escrever seu aplicativo e descobrir que sua implementação de chave composta não é filtrada na primeira chave muito rapidamente ou que o mapa de mapas está consumindo muita memória, você deve otimizar.
A otimização prematura é a raiz de todo mal. Não abstrair é pior.
fonte
Ambas as opções não são boas na minha opinião. Diga e se a lógica de negócios mudar novamente para que você tenha outro subtipo?
O que eu sugiro que você faça é o seguinte:
Type
tabela.Type(INT auto_inc id, VARCHAR(255) name, VARCHAR(255) desc, INT status, etc)
Price
tabela, use chave estrangeira naType
tabela acima, para que pareçaPrice(INT type_id, INT price)
inactive
porque , devido às referências pendentes, a exclusão seria uma verdadeira dor de cabeçaAgora, a lógica do usuário e da empresa pode adicionar qualquer combinação de subtipo de tipo ao esquema. O que eles precisariam fazer é simplesmente criar uma nova linha na
Type
tabela e alterarname
e / oudesc
na tabela antiga.No seu caso, o usuário seria renomear tipo
A
para tipoA_1
, adicionar um novo tipo chamadoA_2
e definir novo preço paraA_2
a15
fonte