Mapas aninhados vs. chaves combinadas

5

No projeto em que estou trabalhando atualmente, tivemos três tipos diferentes de preços, dependendo da idade do usuário (adulto, criança, etc ...). Então nós tínhamos no DB uma tabela parecida com essa:

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

No começo, tínhamos apenas 4 tipos diferentes de preços, então, no código, tínhamos algo assim:

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 assim:

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 melhor e por quê?

Mapas aninhados

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

em que as chaves são o tipo e o subtipo de preço:

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

Chaves combinadas

O mesmo mapa que o original:

Map<String, BigDecimal> prices;

E concatene as chaves para indexar os diferentes preços:

prices.get(type+"_"+subtype);
    
por mario595 16.07.2014 / 15:16
fonte

4 respostas

4

As chaves aninhadas e combinadas têm seus lugares. bowmore fornece um argumento pro para chaves compostas e um argumento para mapas aninhados. Deixe-me fornecer a oposição leal:

As chaves do mapa composto funcionam muito bem quando você procura um item específico 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. . Uma vez que o usuário ou algoritmo ou o que quer que escolha A, então você precisa saber apenas sobre os subtipos de A, e B..Z ou B..ZZZZZ não importam mais.

Agora, você está lidando com uma estrutura de pesquisa muito precisa e eficiente para a pesquisa. Se você tentar fazer isso com chaves compostas, acabará fazendo uma varredura completa na tabela a la [ (key, value) for (key, value) in prices.items() if key.startswith('A') ] . Isso não é uma operação eficiente e será lento se o mapa for grande demais.

Os 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 de (type, subtype, variation) ou (type, subtype, version) ? Nesse caso, uma abordagem de mapeamento aninhado pode ser estendida de maneira limpa. Isso, no entanto, é uma vantagem estilística de segunda ordem, especialmente em comparação com a vantagem da "fácil pesquisa" acima.

    
por 17.07.2014 / 21:58
fonte
3

Evite mapas aninhados. Eles são mais difíceis de dimensionar e levam a códigos muito verbosos e difíceis de ler com todas as declarações de genéricos aninhados em andamento.

Mais importante, os mapas em Java tendem a consumir muita memória. Preencher um mapa com ainda mais mapas só agravará o problema de consumo de memória.

Por fim, é mais fácil avaliar um mapa que usa chaves compostas.

O uso de chaves compostas facilitará a sua vida nos casos mais comuns, mas algumas coisas serão mais difíceis. Obter todos os preços para 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.

    
por 17.07.2014 / 19:00
fonte
0

Ambas as opções não são boas na minha opinião. Diga o que acontece 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:

  1. Use uma chave substituta para uma chamada de tabela Type esta tabela seria semelhante a Type(INT auto_inc id, VARCHAR(255) name, VARCHAR(255) desc, INT status, etc)
  2. Na sua tabela Price , use a chave estrangeira para a tabela Type acima, para que ela pareça Price(INT type_id, INT price)
  3. Assegure-se de não apoiar a exclusão de um tipo. Você simplesmente marca um tipo como inactive porque as referências pendentes tornariam a exclusão uma verdadeira dor de cabeça

Agora, a lógica de usuário e de negócios pode incluir qualquer tipo de combinação de subtipo no esquema. O que eles precisariam fazer é simplesmente criar uma nova linha na tabela Type e alterar name e / ou desc na tabela antiga.

No seu caso, o usuário renomeia o tipo A para digitar A_1 , adiciona um novo tipo chamado A_2 e define novo preço para A_2 as 15

    
por 16.07.2014 / 19:48
fonte
0

Isso tem menos a ver com "qual implementação é melhor" e mais com "com qual abstração eu deveria trabalhar".

Tanto a chave composta quanto o mapa de mapas têm seus pontos strongs e fracos, os quais residem dentro do domínio de 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 que você precisa nela e use isso em seu código. Em seguida, escreva qualquer implementação que você possa escrever mais rápido e apenas siga em frente.

APENAS uma vez que você tenha escrito sua aplicação e você descobriu que a sua implementação de chave composta não filtra a primeira tecla muito rapidamente, ou que o mapa de mapas está tendo muita memória, você deve ir e otimizar.

A otimização prematura é a raiz de todo o mal. Não abstrair é pior.

    
por 28.09.2016 / 11:24
fonte