Autor Beitrag
BlackMatrix
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 243
Erhaltene Danke: 1



BeitragVerfasst: Fr 04.11.11 20:48 
Guten Abend.

Ich möchte gerne, dass zu meiner generischen Liste<class> ein Element nur dann hinzugefügt wird, wenn eine Property der Klasse in der Liste noch nicht vorhanden ist.

Etwas kompliziert? Hier mein bisheriger Code:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
List<Klasse> Klassen = new List<Klasse>();

        public bool AddIfNotExists(Klasse klasse)
        {
            if (!Contains(klasse))
            {
                Proxies.Add(klasse);
                return true;
            }
            return false;
        }

        public bool Contains(Klasse klasse)
        {
            if (Klassen.Where(k => k.Property== klasse.Property).Count() > 0)//wenn mindestens 1 Element mit dieser Property in der Liste
                return true;
            return false;
        }


Das funktioniert zwar soweit, aber wenn ich nun 1000 Elemente hinzufügen würde, dann dauert dies zulange, weil bei jedem Element über diese Linqausdruck geschaut wird, ob schon ein Element vorhanden ist.

Nun meine Frage, wie löst man am besten das Problem?

1. Hält man neben der Liste<Klasse> einfach noch eine Liste mit der Property der Klasse und prüft dann einfach mit der Methode von List nach Contains.
Ist meiner Meinung nach aber sehr umständlich, weil man immer aufpassen muss, dass beide Listen die selben Werte hat und man ja im Grunde doppelt Sachen speichert.
2. Man fügt erstmal alle Element hinzu und löscht am Ende die doppelten? Wobei man dann wieder aufpassen muss, dass man nicht die älteren Elemente entfernt. Unschön finde ich es auch -> erst hinzufügen, dann wieder löschen.
3. <Eure Meinung> ;)

LG
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Fr 04.11.11 21:36 
Hallo!

Ich würde das mit einem Dictionary lösen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
        class Klasse
        {
            public string Property { get; set; }
        }

        class Liste
        {
            private Dictionary<string, Klasse> klassen = new Dictionary<string, Klasse>();

            public bool AddIfNotExists(Klasse k)
            {
                if (klassen.ContainsKey(k.Property))
                    return false;

                klassen.Add(k.Property, k);
                return true;
            }
        }


Grüße,
Christian

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
UGrohne
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Fr 04.11.11 22:53 
Wenn Du zusätzlich IComparable implementierst kannst Du auch mit der Contains-Methode der Collections arbeiten, um zu prüfen, ob das Element schon drin ist. Außer Du brauchst eine genauere Prüfung unabhängig von dem einen Attribut an anderer Stelle.
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Fr 04.11.11 23:06 
user profile iconUGrohne hat folgendes geschrieben Zum zitierten Posting springen:
Wenn Du zusätzlich IComparable implementierst kannst Du auch mit der Contains-Methode der Collections arbeiten, um zu prüfen, ob das Element schon drin ist. Außer Du brauchst eine genauere Prüfung unabhängig von dem einen Attribut an anderer Stelle.
Das ist aber O(n) und ContainsKey ist O(1) :P

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
ujr
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 102
Erhaltene Danke: 12



BeitragVerfasst: Fr 04.11.11 23:10 
user profile iconChristian S. hat folgendes geschrieben Zum zitierten Posting springen:
Ich würde das mit einem Dictionary lösen


oder mit HashSet<T>, weil es zum "Key" keinen "Value" gibt und es entfällt jegliche Prüfung, da .Add Elemente nur einmalig hinzufügt.

PS: Wie kommt eigentlich der OffTopic-Anteil in den Sternchen zustande?
BlackMatrix Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 243
Erhaltene Danke: 1



BeitragVerfasst: Sa 05.11.11 01:02 
user profile iconujr hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconChristian S. hat folgendes geschrieben Zum zitierten Posting springen:
Ich würde das mit einem Dictionary lösen


oder mit HashSet<T>, weil es zum "Key" keinen "Value" gibt und es entfällt jegliche Prüfung, da .Add Elemente nur einmalig hinzufügt.

PS: Wie kommt eigentlich der OffTopic-Anteil in den Sternchen zustande?


HashSet added auch 2 völlig identische Objekte, leider :-/

Christian S. Dictionary finde ich auch nur halbwegs brauchbar. Sollte nun meine Überprüfung auf 2 Properties hinauslaufen muss da ganz schön was geändert werden.

UGrohnes Lösung habe ich nicht ganz verstanden, zumindest den 2. Teil nicht. Würde ich aber denke ich herausfinden, jedoch weiß ich nicht genau, ob Christian S. mit seiner Antwort die Lösungsmöglichkeit schon wieder aufgehoben hat, die verstehe ich nämlich auch nicht so ganz ;)
ujr
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 102
Erhaltene Danke: 12



BeitragVerfasst: Sa 05.11.11 01:18 
user profile iconBlackMatrix hat folgendes geschrieben Zum zitierten Posting springen:
HashSet added auch 2 völlig identische Objekte


Was heißt das? Beachte, dass "2 völlig identische Objekte" immer noch zwei Objekte und nicht ein und das selbe sind. Du musst eben einen HashSet-Konstruktor nehmen, der den IEqualityComparer festlegt.

Poste doch mal Beispielcode.
UGrohne
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Sa 05.11.11 01:25 
user profile iconujr hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBlackMatrix hat folgendes geschrieben Zum zitierten Posting springen:
HashSet added auch 2 völlig identische Objekte


Was heißt das? Beachte, dass "2 völlig identische Objekte" immer noch zwei Objekte und nicht ein und das selbe sind. Du musst eben einen HashSet-Konstruktor nehmen, der den IEqualityComparer festlegt.

Ja, weil zwei Objektinstanzen unterschiedliche Speicherbereiche belegen, der Zeiger zeigt auf unterschiedliche Bereiche und das wird bei neuen Klassen standardmäßig als Vergleich hergenommen. Um das zu ändern müsst Ihr die Methode GetHashCode überschreiben und darin bspw. den Wert des Properties zurückgeben.
BlackMatrix Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 243
Erhaltene Danke: 1



BeitragVerfasst: Sa 05.11.11 01:29 
Das mit dem IEqualityComparer hatte ich noch nicht gewusst, ich werde das morgen einmal ausprobieren.

Wenn ich z.B. 2 Objekte erzeuge, dann möchte ich, dass sich im HashSet nur eins befindet:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
class Person
{
public string Vorname{get;set;}
Person(string vorname){Vorname=vorname;}
}

Person p1=new Person("Hans");
Person p2=new Person("Hans");
HashSet.Add(p1);
HashSet.Add(p2);//soll nicht hinzugefügt werden
UGrohne
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Sa 05.11.11 01:58 
Mit dieser Klassenimplementation würde das funktionieren:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
    class Person:IEquatable<Person>
    {
        public string Vorname { get; set; }
        public Person(string vorname) { Vorname = vorname; }

        public override int GetHashCode()
        {
            return Vorname.GetHashCode();
        }

        public bool Equals(Person other)
        {
            return other.Vorname == this.Vorname;
        }
    }

Und jetzt darf Christian als Experte noch etwas dazu sagen :)
BlackMatrix Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 243
Erhaltene Danke: 1



BeitragVerfasst: Mo 07.11.11 00:00 
Dank dir, ist implementiert und funktioniert.
ujr
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 102
Erhaltene Danke: 12



BeitragVerfasst: Mo 07.11.11 10:55 
Hallo,

ganz richtig ist es aber von der Objektorientierung her nicht. Zwei "Personen" sind eben nicht identisch, bloß weil sie den selben Vornamen haben. Diese Klasse könnte niemals anderweitig eingesetzt werden. Denn vielleicht will man ja woanders nach dem Geburtsdatum entscheiden?

Einen IEqualityComparer zu benutzen wäre meiner Meinung nach die deutlich bessere Lösung. Auch wenn der praktisch fast genauso aussehen würde. Aber man könnte für einen anderen Zweck einen anderen benutzen, die Person-Klasse bliebe aber gleich.
UGrohne
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Mo 07.11.11 11:06 
user profile iconujr hat folgendes geschrieben Zum zitierten Posting springen:
Hallo,

ganz richtig ist es aber von der Objektorientierung her nicht. Zwei "Personen" sind eben nicht identisch, bloß weil sie den selben Vornamen haben. Diese Klasse könnte niemals anderweitig eingesetzt werden. Denn vielleicht will man ja woanders nach dem Geburtsdatum entscheiden?

Einen IEqualityComparer zu benutzen wäre meiner Meinung nach die deutlich bessere Lösung. Auch wenn der praktisch fast genauso aussehen würde. Aber man könnte für einen anderen Zweck einen anderen benutzen, die Person-Klasse bliebe aber gleich.

Da hast Du vollkommen recht. Deswegen auch mein obiger Hinweis, dass dies nur sinnvoll ist, wenn generell damit die Gleichheit dieser Instanzen definiert werden kann.
VampireSilence
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 109
Erhaltene Danke: 5


C# (VS 2008 Express), PHP/MySQL, Windows XP
BeitragVerfasst: Di 08.11.11 11:33 
Ich denke beide Probleme (Wiederverwendbarkeit und Performance) lassen sich am besten mit einem Bisektionsverfahen lösen. Der Vergleichsmethode müsste dann lediglich noch ein Parameter übergeben werden, der die Property qualifiziert, welche als Vergleich dienen soll. Somit könnte man anschließend sowohl gleiche Vornamen, als auch Nachnamen oder sonstwas vergleichen.

Davon abgesehen, könntest du auch für die Kombination aus Vor- und Nachnamen einen Hash vorberechnen, der dann verglichen werden kann, um eben die Kombination ausschließen zu können, ohne 2 gesonderte Vergleiche über die gesamte Liste anstellen zu müssen.

mfg
- VampireSilence