Boa tarde galera, estou me utilizando de uma Hashmap para armazenar usuários conectados a um servidor, até agora tinha a utilizado com sua key sendo do tipo Integer, porém, cheguei numa parte do programa que preciso verificar se há usuários no servidor conectados como uma string, então fiquei com uma dúvida, se eu me utilizar de uma key do tipo Integer, terei que fazer um for pela hashmap e fazer vários métodos para comparações de String como o exemplo dado acima, porém sei que a comparação de String é muito mais demorada, então fico na dúvida em qual usar. Alguém pode me guiar?
O HashMap internamente usa o hashCode do parametro chave como a chave no mapa.
Se a sua String não for exatamente igual, você vai ter um problema de performance quando esse hashMap ficar grande.
Isso por que você vai ter que iterar para saber a forma como você vai comparar. Minha dica é que você padronize a String antes de gravar e na hora de comparar.
private final Map<String, User> users = new HashMap<>();
public final void put(final User user) {
users.put(harmonize(user.getUsername()), user);
// Exemplo, salva com o username = "RafaelGuerreiro ", será normalizado para "RAFAELGUERREIRO"
}
public final User get(String username) {
return users.get(harmonize(username));
// Exemplo, procura com o username = "rafaelguerreiro", será normalizado para "RAFAELGUERREIRO".
}
private String harmonize(String username) {
if (username == null) return null;
return username.trim().toUpperCase(); // esse aqui é só um exemplo.
}
Como o Marky disse, pelo HashMap usar o hashCode() para fazer essa busca, a performance, na verdade, tanto faz.
Não sei se você sabe como o hashCode() funciona no HashMap, mas aqui vai uma explicação mesmo assim:
O método hashCode() retorna um inteiro que é consistente com o estado do seu objeto.
Uma mesma String sempre vai retornar o mesmo hashCode, não importa a forma/data como ela foi criada.
Esse inteiro definirá qual será a "caixa" em que o seu valor vai ser guardado. Dessa forma, quando você precisar do objeto com hashCode() = 30, ele já sabe aonde está a caixa 30, e é lá que ele vai buscar o seu objeto.
Como o Integer tem o range de Integer.MIN_VALUE e Integer.MAX_VALUE, temos 4.294.967.296 inteiros diferentes, criando uma possibilidade virtualmente impossível de ocorrer uma colisão. Detalhe que, ainda assim, se algumas caixas tiverem 2 itens, o método equals() será usado até 2 vezes (pode ser o primeiro). Convenhamos que o java executa 2 comandos equals em uma velocidade praticamente nula.
Colisão seria quando um hashCode() de 2 objetos completamente diferentes são iguais. Rode o exemplo abaixo e você verá um caso de colisão na vida real:
System.out.println("AaAaAa".hashCode());
System.out.println("BBBBBB".hashCode());
Enfim, não vejo desvantagem real de se trocar a chave de Integer para String, desde que você a normalize antes de procurar.
Tome cuidado para não criar um método de normalização que seja muito complexo/lento. Isso vai interferir na sua performance.
[quote=Rafael Guerreiro]Se a sua String não for exatamente igual, você vai ter um problema de performance quando esse hashMap ficar grande.
Isso por que você vai ter que iterar para saber a forma como você vai comparar. Minha dica é que você padronize a String antes de gravar e na hora de comparar.
private final Map<String, User> users = new HashMap<>();
public final void put(final User user) {
users.put(harmonize(user.getUsername()), user);
// Exemplo, salva com o username = "RafaelGuerreiro ", será normalizado para "RAFAELGUERREIRO"
}
public final User get(String username) {
return users.get(harmonize(username));
// Exemplo, procura com o username = "rafaelguerreiro", será normalizado para "RAFAELGUERREIRO".
}
private String harmonize(String username) {
if (username == null) return null;
return username.trim().toUpperCase(); // esse aqui é só um exemplo.
}
Como o Marky disse, pelo HashMap usar o hashCode() para fazer essa busca, a performance, na verdade, tanto faz.
Não sei se você sabe como o hashCode() funciona no HashMap, mas aqui vai uma explicação mesmo assim:
O método hashCode() retorna um inteiro que é consistente com o estado do seu objeto.
Uma mesma String sempre vai retornar o mesmo hashCode, não importa a forma/data como ela foi criada.
Esse inteiro definirá qual será a "caixa" em que o seu valor vai ser guardado. Dessa forma, quando você precisar do objeto com hashCode() = 30, ele já sabe aonde está a caixa 30, e é lá que ele vai buscar o seu objeto.
Como o Integer tem o range de Integer.MIN_VALUE e Integer.MAX_VALUE, temos 4.294.967.296 inteiros diferentes, criando uma possibilidade virtualmente impossível de ocorrer uma colisão. Detalhe que, ainda assim, se algumas caixas tiverem 2 itens, o método equals() será usado até 2 vezes (pode ser o primeiro). Convenhamos que o java executa 2 comandos equals em uma velocidade praticamente nula.
Colisão seria quando um hashCode() de 2 objetos completamente diferentes são iguais. Rode o exemplo abaixo e você verá um caso de colisão na vida real:
System.out.println("AaAaAa".hashCode());
System.out.println("BBBBBB".hashCode());
Enfim, não vejo desvantagem real de se trocar a chave de Integer para String, desde que você a normalize antes de procurar.
Tome cuidado para não criar um método de normalização que seja muito complexo/lento. Isso vai interferir na sua performance.[/quote]
Se eu quiser por exemplo usar um login como key, e esse login não pudesse haver mais de um conectado ao mesmo tempo, bastava eu só jogar no hashmap e retornar o valor?
Nesse caso eu usaria um HashSet.
Para saber se ele já está conectado, eu usaria o logados.contains(“username”);
Para “deslogar”, eu faria logados.remove(“username”);
[quote=Rafael Guerreiro]Nesse caso eu usaria um HashSet.
Para saber se ele já está conectado, eu usaria o logados.contains(“username”);
Para “deslogar”, eu faria logados.remove(“username”);[/quote]
Desculpa, acho que eu não entendi. Por que ele deveria alterar de HashMap para HashSet só pra utilizar Strings?
[quote=Rodrigo Sasaki][quote=Rafael Guerreiro]Nesse caso eu usaria um HashSet.
Para saber se ele já está conectado, eu usaria o logados.contains(“username”);
Para “deslogar”, eu faria logados.remove(“username”);[/quote]
Desculpa, acho que eu não entendi. Por que ele deveria alterar de HashMap para HashSet só pra utilizar Strings?[/quote]
A performance de acesso de ambos é a mesma, mas o map permite acessar um objeto (ex.: Usuário) associada a chave (login). O set é suficiente se vc quer apenas saber quem está logados, pra isso só ver se o set contém o login.
A performance de acesso de ambos é a mesma, mas o map permite acessar um objeto (ex.: Usuário) associada a chave (login). O set é suficiente se vc quer apenas saber quem está logados, pra isso só ver se o set contém o login.[/quote]
Rodrigo, o pfk66 explicou exatamente o porquê eu troquei de HashMap para HashSet. Como ele disse que não poderia ter mais de um usuário logado, eu imaginei que a funcionalidade envolvesse apenas essa verificação.
Ainda assim, se ele for usar Strings não-padronizadas, isso não vai dar certo… Vai ser preciso padronizá-las antes de fazer a verificação. @Nicksf13, isso que eu acabei de dizer vale tanto para HashMap quanto para HashSet.