LINQ – Any() x Contains()

Existem dois tipos de developer: os que apenas querem fazer funcionar e os que querem entender o que estão fazendo para fazer funcionar direito (leia-se bem feito).

Se você é o segundo, provavelmente já se questionou em algum momento quando se usa Any() ou Contains() nas consultas com LINQ. É claro que a resposta é: depende! (hehe). Em grande parte dos casos o resultado será o mesmo e o comportamento semelhante.

Só para recaptular:

Contains() é um método e o seu desempenho depende muito do próprio uso. Em um List tem complexidade O(n), enquanto em um HashSet seria O(1).

Any() é um método de extensão, é mais flexível. Podemos aplicar uma lambda/delegate. Isto teria uma complexidade de O(n).

Vale lembrar que Contains() também possui um método de extensão IEnumerable<T>, que pensando bem, possui uma instância em quase todas coleções.

Se você for fazer algum benchmark, verá que em grande parte das vezes o Any() é um pouco mais lento que Contains() porque não precisa invocar um delegate para cada elemento, ele utiliza a instância Equals. (apenas e se o tipo T não implementa a interface IEquatable<T>) Mas no final das contas se você olhar para a complexidade uma a uma, esta performance será quase a mesma. Agora acho que deu pra entender o porque da resposta “depende”.  =)

Se ainda não deu pra entender, veja bem:

Any()
Determina se qualquer elemento de uma seqüência satisfaz uma condição.
IEnumerable.Any(Extension method)
colecao.Any(i => i == 1);

Contains()
Determina se um elemento está em uma coleção.
IEnumerable.Contains(Object Method)
colecao.Contains(1);

E ainda também temos o Exists(), que não falamos aqui. Ele determina se a List(T) contém elementos que atendam às condições definidas por uma condição.
List.Exists (Object method)

Mas nem tudo são flores.
Usando Contains() com Entity Framework/NHibernate a saída SQL será transcrita para a clausula “WHERE IN”. E aí que mora o perigo, pois isto afeta o desempenho da aplicação, além do problema de alguns bancos como o Oracle que tem o limite de 1000 na clausula IN (ou seja, passou disso você tem 2 problemas).

Para o Entity Framework 6 o problema de performance utilizando Contains foi melhorado, quanto à clausula IN fique atento nos testes para atender o seu cenário e caso seja preciso um Join resolve.

Já passou por algo semelhante? Está usando o EF6? Mande seu feedback. =)
Abraço.

<Post atualizado (em vermelho) com a contribuição do Rodrigo Vidal e Abner das Dores.>

[Code Ramp UP] – Removendo vírgula no final de uma string

Olá! Baseando sempre em fatos, compartilho com vocês uma experiência simples e que boa parte dos developers esquecem que existe ou não sabem o uso quando vão trabalhar com strings.

Vamos lá.

Já vi em alguns métodos que recebem Strings com algum “caracter inútil” no final, que geralmente é resultado de uma concatenação, por exemplo a vírgula abaixo:

"pêra, uva, maçã, "

Um código que sempre aparece nessa hora é algo como abaixo:

StringBuilder sb = new StringBuilder("1, 2, 3, ");   

string resultado1 = sb.ToString(0, sb.Length - 2);
Console.WriteLine(resultado1);

E dá para resolver de maneira bem mais elegante e eficiente com o TrimEnd():

string resultado2 = sb.ToString().TrimEnd(',', ' ');
Console.WriteLine(resultado2);

Para melhorar o entendimento, imagine que você desenvolveu método que espera um array de até 4 parâmetros, concatena com vírgulas e monta uma string afim de imprimir em tela como abaixo:

private static string FormataString(Object[] vetor)
{
	StringBuilder parametros = new StringBuilder();
	parametros.AppendFormat("{0}, {1}, {2}, {3}", vetor);

	string frase = "Times escolhidos: ";
	StringBuilder fraseCompleta = new StringBuilder();
	fraseCompleta.AppendFormat("{0} {1}", frase, parametros);

	return fraseCompleta.ToString().TrimEnd(',', ' ');
}

Assim sendo, você poderá utilizar de um até quatro parâmetros sem precisar se preocupar com aquela “vírgula” do final.

Object[] vetor = {"Goiás", "Vila Nova", "Atlético", null};
Console.WriteLine(FormataString(vetor));

Dê uma olhada também no TrimStart(), para fazer o mesmo, só que no início da string. E para enfatizar, lembre-se que concatenações de string sempre com o StringBuilder, ok?! Comente e dê sugestões para o Code Ramp Up!

Não sabe o que é o Code Ramp Up?!
Leia aqui.

Agregando conhecimento com Code Ramp Up

coderampupCompartilhar experiência e conhecimento sempre é legal, mesmo que seja de um assunto conhecido entre os desenvolvedores. O que percebemos é que por mais que se saiba, ainda há muito para aprender!

Em palestras e aulas aulas que ministro, e até mesmo por e-mail, respondo perguntas cotidianas e sempre achei que seria interessante publicar estas respostas. Categorizei e vou chamar este tipo de post de CODE RAMP UP. E daqui pra frente serão identificados no título do post e nas tags. Fiz até providenciei uma logo para as publicações.

Alguns pontos interessantes:

  • É para desenvolvedores.
  • É para aspirantes, iniciantes e medianos na tecnologia .NET.
  • Não tem uma linha específica de abordagem (ainda). Portanto os assuntos serão aleatórios (podendo ser de Azure hoje e no próximo post ser de Windows Phone).
  • Os posts são de leitura rápida (bom para ler na hora do almoço).
  • Frequência de 1 post por semana, no mínimo.

De toda forma, é algo que já fazia antes. Porém vi que os posts estavam ficando sem nexo ao contexto do meu blog. Posso dizer que o Code Ramp Up também é uma forma destes artigos ficarem organizados por aqui.

Vai ser divertido!
Espero que gostem e aceito sugestões.