1. Explicação
Em relação ao método Map traversal em Java, muitos artigos recomendam o uso de entrySet, que é considerado muito mais eficiente que keySet. O motivo é: o método entrySet obtém o conjunto de todas as chaves e valores de uma vez, enquanto o keySet obtém apenas o conjunto de chaves. Para cada chave, o valor deve ser pesquisado no Mapa mais uma vez; eficiência geral. Então, qual é a situação real?
Para entender a real lacuna no desempenho de travessia, incluindo as diferenças em diferentes cenários, como travessia de chave + valor, travessia de chave, travessia de valor, etc., tentei realizar alguns testes comparativos.
2. Testes comparativos
No início, apenas um teste simples foi realizado, mas os resultados mostraram que o desempenho do keySet foi melhor, o que me intrigou. Todos não dizem que o entrySet é obviamente melhor que o keySet? Para verificar ainda mais, diferentes dados de teste foram usados para testes comparativos mais detalhados.
2.1 Dados de teste
2.1.1 Dados de teste HashMap
HashMap-1, o tamanho é 1 milhão, a chave e o valor são String, o valor da chave é 1, 2, 3...1000000:
Copie o código do código da seguinte forma:
Mapa<String, String> mapa = new HashMap<String, String>();
Chave de string, valor;
para (i = 1; i <= num; i++) {
chave = "" + eu;
valor = "valor";
map.put(chave, valor);
}
HashMap-2, o tamanho é 1 milhão, a chave e o valor são String, o valor da chave é 50, 100, 150, 200,..., 50000000:
Copie o código do código da seguinte forma:
Mapa<String, String> mapa = new HashMap<String, String>();
Chave de string, valor;
para (i = 1; i <= num; i++) {
chave = "" + (i * 50);
valor = "valor";
map.put(chave, valor);
}
2.1.2 Dados de teste do TreeMap
TreeMap-1, o tamanho é 1 milhão, a chave e o valor são String, o valor da chave é 1, 2, 3...1000000:
Copie o código do código da seguinte forma:
Mapa<String, String> mapa = new TreeMap<String, String>();
Chave de string, valor;
para (i = 1; i <= num; i++) {
chave = "" + eu;
valor = "valor";
map.put(chave, valor);
}
TreeMap-2, o tamanho é 1 milhão, a chave e o valor são String, os valores-chave são 50, 100, 150, 200,..., 50000000, mais discretos:
Copie o código do código da seguinte forma:
Mapa<String, String> mapa = new TreeMap<String, String>();
Chave de string, valor;
para (i = 1; i <= num; i++) {
chave = "" + (i * 50);
valor = "valor";
map.put(chave, valor);
}
2.2 Cenário de teste
Use vários métodos de escrita de keySet, entrySet e valores para testar três cenários: passagem de chave + valor, passagem de chave e passagem de valor.
2.2.1 Atravessar chave+valor
keySet percorre chave+valor (método de escrita 1):
Copie o código do código da seguinte forma:
Iterador<String> iter = map.keySet().iterator();
enquanto (iter.hasNext()) {
chave = iter.next();
valor = mapa.get(chave);
}
keySet percorre chave+valor (método de escrita 2):
Copie o código do código da seguinte forma:
for (chave de string: map.keySet()) {
valor = mapa.get(chave);
}
entrySet percorre chave+valor (método de escrita 1):
Copie o código do código da seguinte forma:
Iterador<Entry<String, String>> iter = map.entrySet().iterator();
Entrada<String, String> entrada;
enquanto (iter.hasNext()) {
entrada = iter.next();
chave = entrada.getKey();
valor = entrada.getValue();
}
entrySet percorre chave+valor (método de escrita 2):
Copie o código do código da seguinte forma:
for (Entry<String, String> entrada: map.entrySet()) {
chave = entrada.getKey();
valor = entrada.getValue();
}
2.2.2 Tecla de deslocamento
keySet percorre a chave (método de escrita 1):
Copie o código do código da seguinte forma:
Iterador<String> iter = map.keySet().iterator();
enquanto (iter.hasNext()) {
chave = iter.next();
}
keySet percorre a chave (método de escrita 2):
Copie o código do código da seguinte forma:
for (chave de string: map.keySet()) {
}
entrySet atravessa a chave (método de escrita 1):
Copie o código do código da seguinte forma:
Iterador<Entry<String, String>> iter = map.entrySet().iterator();
enquanto (iter.hasNext()) {
chave = iter.next().getKey();
}
entrySet atravessa a chave (método de escrita 2):
Copie o código do código da seguinte forma:
for (Entry<String, String> entrada: map.entrySet()) {
chave = entrada.getKey();
}
2.2.3 Valor transversal
keySet percorre o valor (método de escrita 1):
Copie o código do código da seguinte forma:
Iterador<String> iter = map.keySet().iterator();
enquanto (iter.hasNext()) {
valor = map.get(iter.next());
}
keySet percorre o valor (método de escrita 2):
Copie o código do código da seguinte forma:
for (chave de string: map.keySet()) {
valor = mapa.get(chave);
}
entrySet percorre o valor (método de escrita 1):
Copie o código do código da seguinte forma:
Iterador<Entry<String, String>> iter = map.entrySet().iterator();
enquanto (iter.hasNext()) {
valor = iter.next().getValue();
}
entrySet percorre o valor (método de escrita 2):
Copie o código do código da seguinte forma:
for (Entry<String, String> entrada: map.entrySet()) {
valor = entrada.getValue();
}
valores atravessa valor (método de escrita 1):
Copie o código do código da seguinte forma:
Iterador<String> iter = map.values().iterator();
enquanto (iter.hasNext()) {
valor = iter.next();
}
valores atravessa valor (método de escrita 2):
Copie o código do código da seguinte forma:
for (valor da string: map.values()) {
}
2.3 Resultados do teste
2.3.1 Resultados do teste HashMap
Unidade: milissegundo | HashMap-1 | HashMap-2 |
| keySet percorre chave + valor (método de escrita 1) | 39 | 93 |
| keySet percorre chave + valor (método de escrita 2) | 38 | 87 |
| entrySet percorre chave + valor (método de escrita 1) | 43 | 86 |
| entrySet percorre chave + valor (método de escrita 2) | 43 | 85 |
Unidade: milissegundo | HashMap-1 | HashMap-2 |
| keySet atravessa a chave (método de escrita 1) | 27 | 65 |
| keySet percorre a chave (método de escrita 2) | 26 | 64 |
| entrySet atravessa a chave (método de escrita 1) | 35 | 75 |
| entrySet atravessa a chave (método de escrita 2) | 34 | 74 |
Unidade: milissegundo | HashMap-1 | HashMap-2 |
| keySet percorre valor (método de escrita 1) | 38 | 87 |
| keySet percorre valor (método de escrita 2) | 37 | 87 |
| entrySet atravessa valor (método de escrita 1) | 34 | 61 |
| entrySet atravessa valor (método de escrita 2) | 32 | 62 |
| valores atravessam valor (método de escrita 1) | 26 | 48 |
| valores atravessam valor (método de escrita 2) | 26 | 48 |
2.3.2 Resultados do teste TreeMap
Unidade: milissegundo | TreeMap-1 | TreeMap-2 |
| keySet percorre chave + valor (método de escrita 1) | 430 | 451 |
| keySet percorre chave + valor (método de escrita 2) | 429 | 450 |
| entrySet percorre chave + valor (método de escrita 1) | 77 | 84 |
| entrySet percorre chave + valor (método de escrita 2) | 70 | 68 |
Unidade: milissegundo | TreeMap-1 | TreeMap-2 |
| keySet percorre a chave (método de escrita 1) | 50 | 49 |
| keySet percorre a chave (método de escrita 2) | 49 | 48 |
| entrySet atravessa a chave (método de escrita 1) | 66 | 64 |
| entrySet atravessa a chave (método de escrita 2) | 65 | 63 |
Unidade: milissegundo | TreeMap-1 | TreeMap-2 |
| keySet percorre valor (método de escrita 1) | 432 | 448 |
| keySet percorre valor (método de escrita 2) | 430 | 448 |
| entrySet atravessa valor (método de escrita 1) | 62 | 61 |
| entrySet atravessa valor (método de escrita 2) | 62 | 61 |
| valores atravessam valor (método de escrita 1) | 46 | 46 |
| valores atravessam valor (método de escrita 2) | 45 | 46 |
3. Conclusão
3.1 Se você usa HashMap
1. Ao percorrer chave e valor ao mesmo tempo, a diferença de desempenho entre os métodos keySet e entrySet depende das condições específicas da chave, como complexidade (objetos complexos), discrição, taxa de conflito, etc. Em outras palavras, depende do custo de procurar valor no HashMap. A operação do entrySet para recuperar todas as chaves e valores de uma vez tem uma sobrecarga de desempenho. Quando essa perda é menor que a sobrecarga do HashMap em busca de valor, a vantagem de desempenho do entrySet será refletida. Por exemplo, no teste de comparação acima, quando a chave é a string numérica mais simples, keySet pode ser mais eficiente, levando 10% menos tempo que entrySet. De modo geral, é recomendado usar o entrySet. Porque quando a chave é muito simples, seu desempenho pode ser um pouco inferior ao keySet, mas é controlável à medida que a chave se torna mais complicada, as vantagens do entrySet serão claramente refletidas; Claro, podemos escolher de acordo com a situação real
2. Ao percorrer apenas chaves, o método keySet é mais apropriado, pois entrySet também retira valores inúteis, o que desperdiça desempenho e espaço. Nos resultados do teste acima, keySet leva 23% menos tempo que o método entrySet.
3. Ao percorrer apenas o valor, usar o método vlaues é a melhor escolha, entrySet é um pouco melhor que o método keySet.
4. Entre os diferentes métodos de escrita transversal, recomenda-se usar o seguinte método de escrita, que é um pouco mais eficiente:
Copie o código do código da seguinte forma:
for (chave de string: map.keySet()) {
valor = mapa.get(chave);
}
for (Entry<String, String> entrada: map.entrySet()) {
chave = entrada.getKey();
valor = entrada.getValue();
}
for (valor da string: map.values()) {
}
3.2 Se você usa TreeMap
1. Ao percorrer chave e valor ao mesmo tempo, ao contrário do HashMap, o desempenho do entrySet é muito maior do que o do keySet. Isso é determinado pela eficiência da consulta do TreeMap. Em outras palavras, o custo de busca de valor no TreeMap é relativamente grande, o que é significativamente maior do que o custo de recuperar todas as chaves e valores de uma vez do entrySet. Portanto, é altamente recomendável usar o método entrySet ao percorrer um TreeMap.
2. Ao percorrer apenas chaves, o método keySet é mais apropriado, pois entrySet também retira valores inúteis, o que desperdiça desempenho e espaço. Nos resultados do teste acima, keySet leva 24% menos tempo que o método entrySet.
3. Ao percorrer apenas valor, usar o método vlaues é a melhor escolha, e entrySet também é obviamente melhor que o método keySet.
4. Entre os diferentes métodos de escrita transversal, recomenda-se usar o seguinte método de escrita, que é um pouco mais eficiente:
Copie o código do código da seguinte forma:
for (chave de string: map.keySet()) {
valor = mapa.get(chave);
}
for (Entry<String, String> entrada: map.entrySet()) {
chave = entrada.getKey();
valor = entrada.getValue();
}
for (valor da string: map.values()) {
}