Guia do Guava Multiset

Guia do Guava Multiset

1. Visão geral

Neste tutorial, vamos explorar uma das coleçõesGuava -Multiset. Como umjava.util.Set, permite armazenamento e recuperação eficientes de itens sem um pedido garantido.

No entanto, ao contrário deSet, ele permitemultiple occurrences of the same element rastreando a contagem de cada elemento exclusivo que ele contém.

2. Dependência do Maven

Primeiro, vamos adicionarthe guava dependency:


    com.google.guava
    guava
    27.1-jre

3. UsandoMultiset

Vamos considerar uma livraria que possui várias cópias de livros diferentes. Podemos querer realizar operações como adicionar uma cópia, obter o número de cópias e remover uma cópia quando for vendida. As a Set does not allow for multiple occurrences of the same element, it can’t handle this requirement.

Let’s get started by adding cópias de um título de livro. OMultiset deve retornar que o título existe e nos fornecer a contagem correta:

Multiset bookStore = HashMultiset.create();
bookStore.add("Potter");
bookStore.add("Potter");
bookStore.add("Potter");

assertThat(bookStore.contains("Potter")).isTrue();
assertThat(bookStore.count("Potter")).isEqualTo(3);

Agora vamos remover uma cópia. Esperamos que a contagem seja atualizada de acordo:

bookStore.remove("Potter");
assertThat(bookStore.contains("Potter")).isTrue();
assertThat(bookStore.count("Potter")).isEqualTo(2);

E, na verdade,we can just set the count em vez de realizar várias operações de adição:

bookStore.setCount("Potter", 50);
assertThat(bookStore.count("Potter")).isEqualTo(50);

Multiset valida o valorcount. Se definirmos como negativo, umIllegalArgumentException é lançado:

assertThatThrownBy(() -> bookStore.setCount("Potter", -1))
  .isInstanceOf(IllegalArgumentException.class);

4. Comparação comMap

Sem acesso aMultiset, poderíamos realizar todas as operações acima implementando nossa própria lógica usandojava.util.Map:

Map bookStore = new HashMap<>();
// adding 3 copies
bookStore.put("Potter", 3);

assertThat(bookStore.containsKey("Potter")).isTrue();
assertThat(bookStore.get("Potter")).isEqualTo(3);

// removing 1 copy
bookStore.put("Potter", 2);
assertThat(bookStore.get("Potter")).isEqualTo(2);

Quando queremos adicionar ou remover uma cópia usandoMap, precisamos lembrar a contagem atual e ajustá-la de acordo. Também precisamos implementar essa lógica em nosso código de chamada todas as vezes ou construir nossa própria biblioteca para esse fim. Nosso código também precisaria controlar o argumentovalue. Se não tomarmos cuidado, poderíamos facilmente definir o valor comonull ou negativo, embora ambos os valores sejam inválidos:

bookStore.put("Potter", null);
assertThat(bookStore.containsKey("Potter")).isTrue();

bookStore.put("Potter", -1);
assertThat(bookStore.containsKey("Potter")).isTrue();

Como podemos ver, é muito mais conveniente usarMultiset em vez deMap.

5. Concorrência

Quando queremos usarMultiset em um ambiente concorrente, podemos usarConcurrentHashMultiset, que é uma implementaçãoMultiset thread-safe.

Porém, devemos observar que ser seguro para threads não garante consistência. Usar os métodosadd ouremove funcionará bem em um ambiente multi-thread,but what if several threads called the setCount method? 

Se usarmos o métodosetCount,the final result would depend on the order of execution across threads, que não pode ser necessariamente previsto. Os métodosadd andremove são incrementais, eConcurrentHashMultiset é capaz de proteger seu comportamento. Setting the count directly is not incremental and so can cause unexpected results when used concurrently.

No entanto, há outro sabor do métodosetCount que atualiza a contagem apenas se seu valor atual corresponder ao argumento passado. O método retorna true se a operação tiver êxito, uma forma de bloqueio otimista:

Multiset bookStore = HashMultiset.create();
// updates the count to 2 if current count is 0
assertThat(bookStore.setCount("Potter", 0, 2)).isTrue();
// updates the count to 5 if the current value is 50
assertThat(bookStore.setCount("Potter", 50, 5)).isFalse();

Se quisermos usar o métodosetCount em código concorrente, devemos usar a versão acima para garantir a consistência. Um cliente multiencadeado pode executar uma nova tentativa se a alteração da contagem falhar.

6. Conclusão

Neste breve tutorial, discutimos quando e como usar umMultiset, em comparação com umMap padrão e verificamos a melhor forma de usá-lo em um aplicativo simultâneo.

Como sempre, o código-fonte dos exemplos pode ser encontradoover on GitHub.