Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

Como o GetHashCode() determina "categorias" ?

Na Aula 06 - Lidando com conjuntos, foi ensinado que os conjuntos são capazes de performatizar as buscas de elementos através do GetHashCode() que atribui identificadores (ou categorias) para os elementos.

Minha dúvida é:

Imaginemos um conjunto de string ("abc", "casa", "gato", "azul") e que eu queira adicionar um novo elemento "abc". Constatei via teste que, em caso de conjunto de strings, elementos com o valor exatamente igual terão hash´s iguais. Ou seja, a categoria aqui é definida quando elementos possuem o mesmo valor. Dessa forma, antes de adicionar o novo elemento "abc" o C# não vai precisar varrer TODO o conjunto para verificar quais elementos possuem o mesmo valor de "abc" (voltando ao problema das listas) ? Não entendi direito como ele utiliza essa categorização.

3 respostas

O conjunto, por baixo dos panos, em geral, usa um estrutura de dicionário para separar os objetos por categorias. Quando você adicionar outro "abc", ele vai fazer o calculo para saber em qual "bucket" estão os objetos com aquele hashcode. Caso dentro daquele subconjunto exista um objeto com o mesmo valor do passado como argumento, ele não insere.

A velocidade vem daí.. ele não precisa percorrer todos elementos, apenas um subconjunto.

E teria como saber qual a inteligência por trás dos conjuntos para determinar qual "bucket" determinado elemento faz parte ?

Essa inteligência varia dependendo do tipo dos elementos do meu conjunto ?

solução!

Sim!

Você pode olhar a implementação do GetHashCode() para o Object, por exemplo (dá uma checada no Visual Studio, vai dando F12 até chegar lá). Ela redireciona para a ObjectNative::GetHashCode da CLR, como indica esse comentário aqui de uma dúvida semelhante no StackOverflow. O próprio autor sugere dar uma olhada na implementação do C++, que é mais fácil de entender.

O código é bem complexo, mas eles seguem os conceitos de uma função de hash (ou função de espalhamento), que você pode olhar com mais detalhes no ótimo artigo da wikipedia em inglês (o artigo em português não está bom).

Resumindo, você precisa implementar qualquer função que tenha as seguintes propriedades:

Determinismo: Para toda entrada, deve gerar a mesma saída (isso é uma propriedade de qualquer função, no conceito matemático)

Uniformidade: Ela deve distribuir bem as saídas para diferentes entradas, não necessariamente de forma aleatória. Se você tiver duas categorias e jogar todos os números ímpares pra uma categoria e todos os pares para outra, ok.

Imagem definida: O conjunto de saída deve ser bem definido, por exemplo um inteiro de 32 bits. Ela não pode crescer conforme o tamanho de uma string, por exemplo.

E dependendo da sua aplicação, você precisa ter (ou não ter) algumas propriedades. Por exemplo, para criptografia, é imperativo que sua função não seja inversível, ou seja, dada uma saída, não seja possível (na prática) descobrir a entrada.

Isso é só uma introdução. Para mais detalhes, esses links acima irão ajudar bastante!

Respondendo a segunda pergunta, a inteligência é igual a priori, pois ela está implementada em Object, mas nada impede que você sobrescreva esse método nas classes. Sugiro fortemente que você faça isso e experimente para ver o que acontece: 1 - Sobrescreva o GetHashCode de uma classe qualquer (pode ser sua ou não) com um código simples, do tipo "se a string começa com 'a', retorna 1, senão retorna 2" ou "retorna o int módulo 5". 2 - Insira vários elementos desse tipo em um conjunto e faça vários testes, de igualdade, em qual categoria ele caiu, etc.

Com isso você vai aprender bem o que acontece por trás dos panos!

Abraços, bons estudos e divirta-se!