2010-08-31 58 views
7

Comment optimiser ce code?Supprimer foreach - C# optimisation de code

ParentDoglist, ChildDoglistis - Ilist. dogListBox - Liste Boîte

foreach (Dog ParentDog in ParentDoglist) 
{ 
foreach (Dog ChildDog in ChildDoglist) 
{ 
    if(ParentDog.StatusID==ChildDog.StatusID) 
    dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key)); 
} 
} 

EDIT: ParentDogTypeList, DogTypeList ont été renommés comme ParentDoglist, ChildDoglist, où les deux ne sont pas liés les uns aux autres

if(ParentDog.Key==ChildDog.Key) 

a été changé pour

if(ParentDog.StatusID==ChildDog.StatusID) 

Histoire complète:

J'ai besoin de remplir une liste déroulante qui serait réciproque d'une relation parent-enfant. Il y a certains chiens qui n'ont pas d'enfant et qui seraient appelés comme chien-feuille. Et je dois aussi montrer le nombre de chiens dans cette catégorie particulière

DD ressemblerait

Parent1 
    Child11 (10) 
    Child12 (12) 
Parent2 
    Child21 (23) 
    Child22 (20) 
Leaf1 (20) 
Leaf2 (34) 

Ainsi, le ParentDoglist apporterait tous les éléments de l'enfant et de feuilles ainsi que le nombre et ChildDogList aurait Les parents et les feuilles d'identification, donc je serais en mesure de peupler l'enfant respectif à leur parent et lier directement la feuille. Le chien parent, enfant et feuille serait maintenu dans une table et différencié par statusid et count serait dans une autre table.

Aucun parent aurait un chef d'accusation, seul enfant et la feuille auraient comte

Tableau schéma:

alt text

+1

quelle ligne est lente? –

+1

DogTypeList est-il une liste de tous les types de chiens et ParentDogTypeList un sous-ensemble de types de chiens? – gkrogers

+0

@gkrogers Pl regarde mon edit –

Répondre

8

Vous pouvez trier ParentDoglist et ChildDoglist et faire O(n) linéaire algorithme de calcul INSEAD ce O(n^2).

Mais vous pouvez trier les conteneurs dans O((ParentDoglist.Size() + ChildDoglist.Size()) * log2(ParentDoglist.Size() + ChildDoglist.Size())).

Ensuite, si vous exécutez ce code UNE SEULE FOIS, votre algorithme est optimal . Mais si vous cherchez PLUS D'UNE FOIS, la solution optimale est de trier les conteneurs et faire la comparaison en temps linéaire, mais si votre conteneur peut changer bettwen fonctions de recherche a été lanuched et vous en utilisant "plus d'une fois la solution de temps" doit utiliser RB-Tree conteneur pour porter ces éléments, car avec la liste normale après le conteneur a été modifié, vous ne pouvez pas revenir à l'état trié en O(log(n)) temps.

-1
foreach (var ParentDog in ParentDoglist.Where(p=>ChildDoglist.Any(c=>c.Key== p.Key)).ToList()) 
    dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key)); 

Voilà comment vous le feriez avec LINQ

+0

Est-ce que cela n'a pas encore de boucle imbriquée implicite? –

+0

Ceci est toujours O (n^2) ;-(seulement avec foreach foreach dans le conteneur ChildDoglist sans aucune optimisation – Svisstack

2

Votre plus gros problème est probablement le dogListBox.Items.Add. L'ajout de chaque élément un à la fois est assez cher. ListBox.Items.AddRange est plus efficace.

Pour réduire la taille de la boucle interne, vous pouvez créer une recherche des clés dans la boucle interne.

List<ListItem> listItems = new List<ListItem>(); 
ILookup<string, Dog> childDogsPerKey = ChildDoglist.ToLookup(dog => dog.Key); 
foreach (Dog ParentDog in ParentDoglist) 
{ 
    foreach (Dog ChildDog in childDogsPerKey[ParentDog.Key]) 
    { 
     listItems.Add(new ListItem(ParentDog.Name, ParentDog.Key)); 
    } 
} 
dogListBox.Items.AddRange(listItems.ToArray()); 

Ce code suppose que plusieurs enfants peuvent avoir la même clé. S'il ne peut y avoir qu'un seul chien d'enfant par clé, vous pouvez utiliser .ToDictionary() au lieu

+0

Accepter que AddRange sera probablement beaucoup aider –

1

Étant donné que cela provient de la base de données, les allers-retours de base de données sont susceptibles d'être une perte de performance. La comparaison ParentDog.Key==ChildDog.Key peut également être effectuée en SQL, vous ne devez donc pas extraire toutes ces données dans votre application pour les supprimer.

Modifiez cette option afin de ne sélectionner qu'une seule fois pour saisir toutes les données dans une requête.

Albin a mentionné AddRange, mais vous pouvez même aller plus loin et virtualiser votre grille afin qu'elle ne tire que les lignes affichées à l'utilisateur lorsqu'elle regarde cette partie de la grille.

EDIT

Pour générer votre liste, il semble que vous avez besoin de retourner quelque chose comme ceci à partir du DB:

 
Parent1, null, null 
Parent1, Child1, 110 
Parent1, Child12, 12 
Parent2, null, null 
Parent2, Child21, 23 
Parent2, Child22 ,20 
Leaf1, null, 20 
Leaf2, null, 34 

Cela ressemble à vous besoin d'une sorte de left join et un count, avec

+0

Edited ma question pour expliquer le sceanrio complet –

+0

oui exactement mais comment faire je peupler la zone de liste sans aucune boucle? –

+0

@Sri, une seule requête et une seule boucle, je ne peux plus aider sans un schéma de table et la saveur SQL que vous utilisez (en supposant SQL Server) –

1

Ce n'est pas le foreach qui est lent, il ajoute et rend de nouveaux objets.

Ajouter BeginUpdate/EndUpdate:

dogListBox.BeginUpdate(); 
foreach (Dog ParentDog in ParentDoglist) 
{ 
foreach (Dog ChildDog in ChildDoglist) 
{ 
    if(ParentDog.Key==ChildDog.Key) 
    dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key)); 
} 
} 
dogListBox.EndUpdate(); 
2

Je pense toujours la façon la plus élégante et optimisée est d'utiliser LINQ pour elle.

box.Items.AddRange(
    ParentDoglist.Where(p=>ChildDoglist.Any(c=>c.StatusID== p.StatusID)) 
    .Select(r=>new ListItem(r.StatusID, r.Name)).ToArray()); 

C'est tout et ce n'est qu'une ligne. Si vous préférez les jointures, vous pouvez le faire avec cette requête.

box.Items.AddRange(
    ParentDoglist.Join(ChildDoglist, p => p.StatusID, c => c.StatusID, (p,c)=>p) 
    .Select(r=>new ListItem(r.StatusID, r.Name)).ToArray()); 
0

Vous pouvez remplacer la boucle foreach imbriquée par une simple expression Linq. Pour que cela fonctionne, vous devez utiliser System.Linq;

foreach (Dog ParentDog in 
      (from dog in ParentDogList 
      from ChildDog in dog.StatusId 
      where dog.StatusId == ChildDog.StatusId) 
      select dog)) 
{ 
    dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key)); 
}