Uso e precauções do Guava HashMultimap

Continue a criar, acelere o crescimento! Este é o primeiro dia da minha participação no "Nuggets Daily New Plan · June Update Challenge", clique para ver os detalhes do evento

Uso e precauções do Guava HashMultimap

Olá a todos manhã|tarde|tarde|noite|dia, bom dia, sou um cinza, hoje vou apresentar a vocês um ponto de conhecimento relativamente básico HashMultmap;

Basicamente pode-se dizer que o Guava é um pacote que provavelmente será introduzido em um projeto de desenvolvimento Java. O protagonista apresentado hoje é um container especial HashMultmap, cuja estrutura de dados pode ser entendida simplesmente comoMap<K, Set<V>>

Então, por que de repente pensei em apresentá-lo, porque ontem, porque não entendi profundamente o suficiente, usei-o como estava e Map<K, List<V>>acabou sendo um problema; nesse caso, vamos fazer um bom jogo e refletir nele.

1. Introdução ao modelo de dados

Normalmente, ao usar um novo objeto de dados, devemos primeiro entender seu modelo de dados;

Olhando diretamente para o código-fonte, você descobrirá que a estrutura dos dados armazenados reais éMap<K, Collection<V>>

abstract class AbstractMapBasedMultimap<K, V> extends AbstractMultimap<K, V> implements Serializable {
    private transient Map<K, Collection<V>> map;
}
复制代码

Existem também muitas implementações de Map no jdk, então qual é?

A partir do método de construção, vamos dar uma olhada no processo de inicialização deste membro do mapa

private HashMultimap(int expectedKeys, int expectedValuesPerKey) {
    super(Platform.newHashMapWithExpectedSize(expectedKeys));
    this.expectedValuesPerKey = 2;
    Preconditions.checkArgument(expectedValuesPerKey >= 0);
    this.expectedValuesPerKey = expectedValuesPerKey;
}

private HashMultimap(Multimap<? extends K, ? extends V> multimap) {
    super(Platform.newHashMapWithExpectedSize(multimap.keySet().size()));
    this.expectedValuesPerKey = 2;
    this.putAll(multimap);
}
复制代码

O ponto chave é Platform.newHashMapWithExpectedSizeque amigos familiares conseguiram dar respostas rapidamente, este mapa é o que costumamos usarHashMap

A próxima coisa a se prestar atenção é a Collection no valor, que tipo de container é; para isso, está localizado a partir do momento em que o elemento é adicionadoput(key, value)

O código-fonte chave é o seguinte

public boolean put(@Nullable K key, @Nullable V value) {
    Collection<V> collection = (Collection)this.map.get(key);
    if (collection == null) {
        collection = this.createCollection(key);
        if (collection.add(value)) {
            ++this.totalSize;
            this.map.put(key, collection);
            return true;
        } else {
            throw new AssertionError("New Collection violated the Collection spec");
        }
    } else if (collection.add(value)) {
        ++this.totalSize;
        return true;
    } else {
        return false;
    }
}
复制代码

Acredita-se que este método de escrita seja familiar a todos. Quando existe, é adicionado diretamente ao container; quando não existe, é createCollectionusado para criar um container e inseri-lo em um Map; sua lógica de implementação específica é a seguinte

// com.google.common.collect.HashMultimap#createCollection
Set<V> createCollection() {
    return Platform.newHashSetWithExpectedSize(this.expectedValuesPerKey);
}
复制代码

Portanto, o armazenamento de dados subjacente do HashMultimap é nosso velho amigoHashMap<K, HashSet<V>>

2. Introdução simples

Basicamente, a postura de uso do HashMultimap é muito simples. Segue um exemplo simples para demonstrar. Basicamente, você verá.

2.1 Criação de Contêiner

// 创建一个默认的 HashMap<String, Set<Integer>>,容器的初始化容量与HashMap的默认值一样
HashMultimap<String, Integer> map = HashMultimap.create();

// 当我们知道容器的个数时,推荐使用下面这种方式, 
// HashMap 设置容量为8, 每个HashSet的容量初始化为16
HashMultimap<String, Integer> map2 = HashMultimap.create(8, 16);

// 另外一个就是基于MultMap来创建的case了
HashMultimap<String, Integer> map3 = HashMultimap.create(map);
复制代码

Observe a terceira implementação acima, o que precisa ser entendido é quemap3.get(key) != map.get(key)

Ou seja, o novo contêiner inicializado com base no contêiner original, seu valor é um novo objeto contêiner e todos os elementos no valor anterior são inseridos no novo contêiner, não no objeto contêiner que é referenciado diretamente (dessa forma, é pense mais em uma cópia profunda, não uma cópia superficial?)

2.2 Adicionando elementos

// 添加单个元素
map.put("hello", 510);


// 添加多个元素
map.putAll("skill", Arrays.asList(1, 2, 3, 4, 1));
复制代码

Perceber

  • 因为value是HashSet,所以重复的元素会忽略
  • 塞入重复的元素会忽略
  • 再次申明,添加重复的元素会忽略

(没错,我就是这里出了问题......)

2.3 移除元素

// 移除skill对应的集合中,value=3的元素
map.remove("skill", 3);

// 移除key
map.removeAll("hello");
复制代码

2.4 替换元素

如果我们希望将整个value都换成一个新的集合,那么可以使用replaceValue

// 直接替换skill对应的value集合,新的值为 {100, 200, 300}
map.replaceValues("skill", Arrays.asList(100, 200, 300));
复制代码

2.5 获取元素及遍历

// 获取对应的value集合,当不存在时,返回空集合(不是null,简直是贴心)
Set<Integer> set = map.get("skill");
复制代码

foreach方式的迭代

for (Map.Entry<String, Integer> entry: map.entries()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}
复制代码

注意上面的迭代成员 Map.Entry<String, Integer>,其key依然是HashMap的key,而value则是这个集合中的没一个元素,比如容器中的值为("skill": [100,200,300])时,此时输出如下

skill:200
skill:100
skill:300
复制代码

2.6 输出所有的key

// 输出所有的key,
map.keys()

// 输出key集合
map.keySet();
复制代码

他们两有啥区别?看个实例

HashMultimap<String, Integer> map = HashMultimap.create();
map.replaceValues("skill", Arrays.asList(100, 200, 300));
System.out.println("keys=" + map.keys());
System.out.println("keySet=" + map.keySet());
复制代码

输出如下

keys=[skill x 3]
keySet=[skill]
复制代码

上面这个skill x 3是什么鬼,实际上表示skill有三个,返回的容器可以理解为List,不去重

而下面的KeySet()则返回的是个Set,会去重

2.7 输出所有的value

map.values()
复制代码

通过上面的再理解这个就简单了,所有的value都合并再一个List,接下来我们看一下两种遍历方式

HashMultimap<String, Integer> map = HashMultimap.create();
map.putAll("skill", Arrays.asList(100, 200, 300));
map.put("a", 100);

for (Integer v: map.values()) {
    System.out.println(v);
}
复制代码

实际输出如下

100
100
200
300
复制代码

3. 小结

这里主要介绍的是Gauva的容器HashMultimap的数据模型及使用姿势,知识点相对来说比较基础,再实际使用的时候,请牢记,把它看作是简单方便易使用的 HashMap<K, HashSet<V>> 即可,重点注意value中的元素不能重复即可

那么当我们希望value是个List时,可以怎么整呢?

  • 此时可以使用 LinkedMultiValueMap 来替代,它的底层数据结构实际就是 HashMap<K, LinkedHashMap<V>>
  • 使用 ArrayListMultimap 也可以,底层数据结构为 HashMap<K, ArrayList<V>>

最后提一句,guava的这几个容器的实现,其源码阅读起来不会吃力,且设计思路也非常典型,比如如果让我们自己来基于jdk的基础容器实现一个类似的容器,如何优雅的去实现呢? 这里就给了一个标准答案,强烈推荐有兴趣的小伙伴瞅一下

一灰灰的联系方式

É melhor não ter livros do que não ter livros. O conteúdo acima é puramente de uma família. Devido à capacidade pessoal limitada, há inevitavelmente omissões e erros. Se você encontrar bugs ou tiver sugestões melhores, você pode criticá-los e corrigi-los . Obrigada

  • Site pessoal: blog.hhui.top
  • QQ: Um cinza cinza / 3302797840
  • Conta pública do WeChat: um blog cinza

Acho que você gosta

Origin juejin.im/post/7101648866096709668
Recomendado
Clasificación