sexta-feira, 16 de maio de 2008

Coleções Case-Insensitive [Parte 01 - Dicionários]

Como usar dicionários Case-Insensitive


Certa vez me deparei com o problema de criar dicionários onde as chaves fossem Case-Insensitive, isto é, dicionários onde a chave "nda" fosse considerada a mesma chave "NdA".

Existem diversos cenários onde este comportamento é desejável. Imagine, por exemplo, um dicionário que contém o mapeamento nomeDoCampo para ObjetoCampo, que é usado para representar todos o campos de um objeto formulário.

Se nas regras de negócio o nome do campo for Case-Sensitive, isto é, o campo nomeado "Campo01" for considerado diferente do campo nomeado "CAMPO01", não existe segredo, basta usar um Hashtable ou um Dictionary genérico que o problema estará sanado.

Se, pelo contrário, as regras definirem que os nomes de campo "campo01" e "CAMPO01" devem se tratar do mesmo objeto, então seu dicionário, o qual irá conter esse mapeamento, precisa de um comportamento diferenciado.

Vamos ver como seria o trabalho de verificar se um dado campo, supondo que no nome do campo seja case-insensitive, está presente em um dicionário normal, juntamente com a operação de adição de um novo campo.


static Hashtable campos = new Hashtable();
static void insiraCampo(string nome, object campo)
{
if (!campoExiste(nome))
campos.Add(nome, campo);
}
static bool campoExiste(string nomeDoCampo)
{
foreach (DictionaryEntry entrada in campos)
{
if (entrada.Key.ToString().ToUpper().Equals(nomeDoCampo.ToUpper()))
return true;
}
return false;
}

Veja o mesmo exemplo onde a variável campos é um dicionário Case-Insensitive

static Hashtable camposInsensitive = CollectionsUtil.CreateCaseInsensitiveHashtable();
static void insiraCampoCaseInsensitive(string nome, object campo)
{
if (!campos.ContainsKey(nome))
campos.Add(nome, campo);
}
/*
O método campoExiste(string nomeDoCampo) não é mais necessário, basta invocar campos.ContainsKey(nomeDoCampo) para obter o resultado.
*/

O ganho não é apenas na menor quantidade de linhas de código e de um melhor aproveitamento da API .NET, uma das principais vantagens é também o desempenho.

Vejam abaixo as diferenças entre os tempos de inserção usando as duas formas:


Quantidade de Itens Inseridos

100

1000

5000

10000

Tempo

Hashtable normal (ms)

156250

650000

196093750

930468750

Tempo

Hashtable Case-Insensitive(ms)

0

0

0

312500


Baixe o código de testes clicando aqui.

Configurações da máquina de Testes:

  • 1.75 GB de Memória RAM

  • Processador AMD Athlon 64 x2 - Dual Core Processor TK-53


// Forma padrão de se criar um Hashtable Case Insensitive
Hashtable insensitive = CollectionsUtil.CreateCaseInsensitiveHashtable();
/*
Criação de um Hashtable Case Insensitive informando o número máximo de entradas que o objeto suporta.
*/
Hashtable insensitive2 = CollectionsUtil.CreateCaseInsensitiveHashtable(1000);
/*
Criação de um Hashtable Case Insensitive baseado em um Dicionário existente
o dicionário recebido com argumento é não-genérico, mas caso deseje realizar essa operação com um dicionário genérico basta fazer o cast que funcionará corretamente.
*/
IDictionary<object, string> dicionario = new Dictionary<object, string>();
dicionario.Add("chave01", "Qualquer coisa");
dicionario.Add("Chave01", "Outra Coisa");
dicionario.Add("chave02", "Qualquer coisa denovo");
dicionario.Add("Chave02", "Mais uma coisa");
// Dispara um ArgumentException
Hashtable insensitive3 = CollectionsUtil.CreateCaseInsensitiveHashtable(dicionario as IDictionary);

No próximo post irei falar um pouco sobre a manipulação Case-Insensitive de outros tipos de Coleções.

Espero ter contribuído, e fiquem a vontade para comentar e questionar sobre este post!

Abraços

0 comentários: