In meiner Arbeit als Softwareentwickler stoße ich oft auf die Herausforderung, Listen von Objekten in VB.NET zu vergleichen. Gerade bei benutzerdefinierten Klassen wie Auto
, die aus mehreren Eigenschaften bestehen, wird dieser Vergleich komplexer. Es ist nicht nur wichtig, die Listen zu vergleichen, sondern auch zu verstehen, was ein “Gleichsein” in diesem Kontext bedeutet – vergleichen wir die Referenzen oder die Inhalte der Objekte?
Ausgangsbasis
Ausgangsbasis ist eine sauber erstellte Klasse:
' Definiert die Klasse Auto
Public Class Auto
' Private Felder für Marke und Modell
Private _marke As String = String.Empty
Private _modell As String = String.Empty
' Konstruktor für die Klasse Auto
Public Sub New(ByVal marke As String, ByVal modell As String)
' Setzen der internen Felder mit den übergebenen Werten
Me._marke = marke
Me._modell = modell
End Sub
' Öffentliche Eigenschaft für die Marke des Autos
Public Property Marke() As String
' Getter-Methode für die Marke
Get
Return _marke
End Get
' Setter-Methode für die Marke
Set(ByVal value As String)
Me._marke = value
End Set
End Property
' Öffentliche Eigenschaft für das Modell des Autos
Public Property Modell() As String
' Getter-Methode für das Modell
Get
Return _modell
End Get
' Setter-Methode für das Modell
Set(ByVal value As String)
Me._modell = value
End Set
End Property
End Class
Erläuterung:
- Die Klasse
Auto
hat zwei private Felder_marke
und_modell
, die die Marke und das Modell des Autos speichern. - Der Konstruktor
New
initialisiert diese Felder mit den übergebenen Werten. - Die öffentlichen Eigenschaften
Marke
undModell
ermöglichen den Zugriff auf diese Felder von außen. Sie sind mit Gettern und Settern ausgestattet, um die Werte abzurufen und zu setzen.
Hat man nun zwei Listen dieser Klasse und möchte diese miteinander vergleichen, gibt es verschiedene Möglichkeiten, mit unterschiedlichen Ergebnis.
Vergleich der Referenz
Vergleich mit .contains
' Initialisiert eine neue Liste von Auto-Objekten
Dim Autos3 As New List(Of Auto)
' Durchläuft jedes Auto-Objekt in der Liste Autos2
For Each myAuto As Auto In Autos2
' Überprüft, ob das Auto-Objekt in der Liste Autos1 enthalten ist
If Autos1.Contains(myAuto) Then
' Fügt das Auto-Objekt zur Liste Autos3 hinzu, wenn es in beiden Listen vorkommt
Autos3.Add(myAuto)
End If
Next
Erläuterung des Codes:
- Eine neue Liste
Autos3
vom TypAuto
wird initialisiert. - Die
For Each
-Schleife durchläuft jedes Element (myAuto
) in der ListeAutos2
. - Innerhalb der Schleife wird mit der
Contains
-Methode überprüft, ob das aktuelleAuto
-Objekt (myAuto
) auch in der ListeAutos1
vorhanden ist. - Falls das Objekt in beiden Listen vorhanden ist, wird es der Liste
Autos3
hinzugefügt. Dadurch enthältAutos3
am Ende alle Auto-Objekte, die sowohl inAutos1
als auch inAutos2
vorkommen.
Als etwas schönerer generische Funktion:
' Definiert eine generische Funktion zum Vergleichen zweier Listen
Private Function Compare2List_Contains(Of myType)(ByVal lst1 As List(Of myType), ByVal lst2 As List(Of myType)) As List(Of myType)
' Erstellt eine neue Liste, um die übereinstimmenden Elemente zu speichern
Dim lst3 As New List(Of myType)
' Durchläuft jedes Element in der zweiten Liste
For Each Element As myType In lst2
' Überprüft, ob das Element in der ersten Liste enthalten ist
If lst1.Contains(Element) Then
' Fügt das Element zur neuen Liste hinzu, wenn es in beiden Listen enthalten ist
lst3.Add(Element)
End If
Next
' Gibt die Liste mit den übereinstimmenden Elementen zurück
Return lst3
End Function
Erläuterung des Codes:
- Diese Funktion ist generisch, was bedeutet, dass sie mit verschiedenen Datentypen (
myType
) arbeiten kann. - Zwei Listen
lst1
undlst2
vom TypmyType
werden als Parameter übergeben. - Eine neue Liste
lst3
wird erstellt, um alle übereinstimmenden Elemente zu speichern. - Die Funktion durchläuft jedes Element in
lst2
und überprüft, ob dieses Element auch inlst1
vorhanden ist. - Wenn ein Element in beiden Listen vorhanden ist, wird es zur Liste
lst3
hinzugefügt. - Schließlich gibt die Funktion die Liste
lst3
zurück, die alle Elemente enthält, die in beiden Eingabelisten vorhanden sind.
Das funktioniert aber nur unter folgender Voraussetzung:
funktioniert | Dim myAuto As New Auto(“BMW”, “X3”) Autos1.Add(myAuto) Autos2.Add(myAuto) |
funktioniert nicht | Autos1.Add(New Auto(“Volkswagen”, “Passat”)) Autos2.Add(New Auto(“Volkswagen”, “Passat”)) |
Vergleich mit .GetHashCode
Eine andere Methode, wäre über den Vergleich der Elemente mittels .GetHashCode. Dise funktioniert ebenfalls nur bei identischen Referenzen. Als generische Funktion, würde das dann beispielsweise so aussehen:
' Definiert eine generische Funktion zum Vergleichen zweier Listen basierend auf GetHashCode
Private Function Compare2List_GetHashCode(Of myType)(ByVal lst1 As List(Of myType), ByVal lst2 As List(Of myType)) As List(Of myType)
' Erstellt eine neue Liste, um die übereinstimmenden Elemente zu speichern
Dim lst3 As New List(Of myType)
' Durchläuft jedes Element in der ersten Liste
For Each Element As myType In lst1
' Temporäre Variable zur Speicherung des aktuellen Elements
Dim tmpElement As myType = Element
' Überprüft, ob ein Element mit dem gleichen HashCode in der zweiten Liste existiert
If lst2.Exists(Function(x As myType) x.GetHashCode = tmpElement.GetHashCode) Then
' Fügt das Element zur neuen Liste hinzu, wenn ein übereinstimmender HashCode gefunden wird
lst3.Add(Element)
End If
Next
' Gibt die Liste mit den übereinstimmenden Elementen zurück
Return lst3
End Function
Erläuterung des Codes:
- Diese Funktion ist generisch und kann mit verschiedenen Datentypen (
myType
) verwendet werden. - Zwei Listen
lst1
undlst2
vom TypmyType
werden als Parameter übergeben. - Eine neue Liste
lst3
wird erstellt, um alle Elemente zu speichern, die in beiden Listen vorhanden sind. - Die Funktion durchläuft jedes Element in
lst1
und verwendet eineExists
-Lambda-Funktion, um zu überprüfen, ob ein Element mit dem gleichen HashCode inlst2
existiert. - Wenn ein Element in
lst2
mit dem gleichen HashCode gefunden wird, wird es zur Listelst3
hinzugefügt. - Nachdem alle Elemente überprüft wurden, gibt die Funktion die Liste
lst3
zurück. Diese Liste enthält alle Elemente auslst1
, für die ein Element mit dem gleichen HashCode inlst2
existiert.
Vergleich der Inhalte
Einen etwas anderen Weg muss man beschreiten, wenn man die Inhalte vergleichen möchte und nicht nur die Referenz. Eine mit Contains vergleichbare Funktion gibt es nicht.
Vergleich mit .Exists
Das Grundkonzept funktioniert folgendermaßen:
' Initialisiert eine neue Liste von Auto-Objekten
Dim Autos3 As New List(Of Auto)
' Durchläuft jedes Auto-Objekt in der Liste Autos2
For Each myAuto2 As Auto In Autos2
' Erstellt eine temporäre Kopie des aktuellen Auto-Objekts
Dim myAuto2tmp As Auto = myAuto2
' Überprüft, ob ein Auto mit gleicher Marke und Modell nicht in Autos1 existiert
If Not Autos1.Exists(Function(x As Auto) x.Marke = myAuto2tmp.Marke And x.Modell = myAuto2tmp.Modell) Then
' Fügt das Auto-Objekt zur Liste Autos3 hinzu, wenn es nicht in Autos1 existiert
Autos3.Add(myAuto2)
End If
Next
Erläuterung des Codes:
- Eine neue Liste
Autos3
vom TypAuto
wird initialisiert. - Die
For Each
-Schleife durchläuft jedes Element (myAuto2
) in der ListeAutos2
. - Innerhalb der Schleife wird eine temporäre Kopie des aktuellen Elements erstellt, um es im nächsten Schritt zu überprüfen.
- Die
Exists
-Lambda-Funktion überprüft, ob ein Auto mit derselben Marke und demselben Modell wiemyAuto2tmp
in der ListeAutos1
nicht existiert. - Falls kein entsprechendes Auto in
Autos1
gefunden wird, wird das Auto-ObjektmyAuto2
zur ListeAutos3
hinzugefügt. Dadurch enthältAutos3
am Ende alle Auto-Objekte ausAutos2
, die nicht inAutos1
vorhanden sind.
Will man das in eine Funktion packen, steht man jedoch vor dem Problem, dass man nicht weiß welche Properties der Klasse verglichen werden sollen. In diesem Fall muss man die Klasse um eine Overrides Function erweitern, die beispielsweise .toString oder .GetHashCode ersetzt.
Die Klasse Auto, erweitert um ToString als Overrides Function müsste folgendermaßen aussehen:
' Definiert die Klasse Auto
Public Class Auto
' Private Felder für Marke und Modell
Private _marke As String = String.Empty
Private _modell As String = String.Empty
' Konstruktor für die Klasse Auto
Public Sub New(ByVal marke As String, ByVal modell As String)
' Setzen der internen Felder mit den übergebenen Werten
Me._marke = marke
Me._modell = modell
End Sub
' Öffentliche Eigenschaft für die Marke des Autos
Public Property Marke() As String
' Getter-Methode für die Marke
Get
Return _marke
End Get
' Setter-Methode für die Marke
Set(ByVal value As String)
Me._marke = value
End Set
End Property
' Öffentliche Eigenschaft für das Modell des Autos
Public Property Modell() As String
' Getter-Methode für das Modell
Get
Return _modell
End Get
' Setter-Methode für das Modell
Set(ByVal value As String)
Me._modell = value
End Set
End Property
' Überschreibt die ToString-Methode für die Klasse Auto
Public Overrides Function ToString() As String
' Gibt eine formatierte Zeichenkette mit Marke und Modell zurück
Return LCase(Me._marke) & "|" & LCase(Me._modell)
End Function
End Class
Erläuterungen:
- Die Klasse
Auto
verfügt über zwei private Felder:_marke
und_modell
, die jeweils die Marke und das Modell eines Autos speichern. - Der Konstruktor der Klasse nimmt die Marke und das Modell als Parameter entgegen und weist diese Werte den privaten Feldern zu.
- Für die beiden Felder gibt es jeweils öffentliche Eigenschaften (
Marke
undModell
) mit entsprechenden Gettern und Settern, um den Zugriff von außen zu ermöglichen. - Die
ToString
-Methode wird überschrieben, um eine nützlichere Darstellung derAuto
-Instanzen als Zeichenkette zu ermöglichen. Diese Methode gibt eine Kombination aus Marke und Modell in Kleinbuchstaben zurück, getrennt durch ein Pipe-Symbol (|
).
Dann könnte man beispielsweise folgende generische Funktion zum Vergleich verwenden.
' Definiert eine generische Funktion zum Vergleichen zweier Listen basierend auf der ToString-Methode
Private Function Compare2List_Exists(Of myType)(ByVal lst1 As List(Of myType), ByVal lst2 As List(Of myType)) As List(Of myType)
' Erstellt eine neue Liste, um die übereinstimmenden Elemente zu speichern
Dim lst3 As New List(Of myType)
' Durchläuft jedes Element in der ersten Liste
For Each Element As myType In lst1
' Temporäre Variable zur Speicherung des aktuellen Elements
Dim tmpElement As myType = Element
' Überprüft, ob ein Element mit der gleichen ToString-Ausgabe in der zweiten Liste existiert
If lst2.Exists(Function(x As myType) x.ToString = tmpElement.ToString) Then
' Fügt das Element zur neuen Liste hinzu, wenn ein übereinstimmendes Element gefunden wird
lst3.Add(Element)
End If
Next
' Gibt die Liste mit den übereinstimmenden Elementen zurück
Return lst3
End Function
Erläuterung des Codes:
- Die Funktion
Compare2List_Exists
ist generisch, was bedeutet, dass sie für verschiedene Datentypen (myType
) verwendet werden kann. - Zwei Listen
lst1
undlst2
vom TypmyType
werden als Parameter übergeben. - Eine neue Liste
lst3
wird erstellt, um alle Elemente zu speichern, die in beiden Listen vorhanden sind. - Die Funktion durchläuft jedes Element in
lst1
und verwendet eineExists
-Lambda-Funktion, um zu überprüfen, ob ein Element mit der gleichenToString
-Ausgabe inlst2
existiert. - Wenn ein Element in
lst2
mit der gleichenToString
-Ausgabe gefunden wird, wird es zur Listelst3
hinzugefügt. - Nachdem alle Elemente überprüft wurden, gibt die Funktion die Liste
lst3
zurück, die alle Elemente auslst1
enthält, für die ein Element mit der gleichenToString
-Ausgabe inlst2
existiert.
Ausgangslisten bereinigen
Spinnen wir den Gedanken noch ein Stück weiter. Die 2 Listen sollen nun nicht mehr nur auf in beiden vorkommenden Elemente untersucht werden, sondern es sollen auch in beiden Listen, die Elemente, die in beiden Listen vorkommen, entfernt werden.
Geht man davon aus, dass in Liste1 alle Elemente nur einmal vorkommen, in Liste2 es jedoch auch möglich sein kann, dass Elemente mehrfach vorkommen, könnte man die Listen folgendermaßen bereinigen:
' Definiert eine generische Funktion, um zwei Listen zu vergleichen, übereinstimmende Elemente zu speichern und diese aus den Originallisten zu entfernen
Private Function Compare2List_andRemove(Of myType)(ByRef lst1 As List(Of myType), ByRef lst2 As List(Of myType)) As List(Of myType)
' Liste zur Speicherung der übereinstimmenden Elemente
Dim lst3 As New List(Of myType)
' Initialisierung des Index für die Durchlauf-Überprüfung
Dim i As Integer = 0
' Liste zur temporären Speicherung der gefundenen Elemente
Dim foundItems As New List(Of myType)
' Durchläuft die erste Liste, solange der Index kleiner als die Anzahl der Elemente in lst1 ist
Do
' Speichert das aktuelle Element der ersten Liste in einer temporären Variable
Dim tmpElement1 As myType = lst1(i)
' Findet alle Elemente in der zweiten Liste, die mit dem aktuellen Element der ersten Liste übereinstimmen
foundItems = lst2.FindAll(Function(x As myType) x.ToString = tmpElement1.ToString)
' Überprüft, ob übereinstimmende Elemente gefunden wurden
If foundItems.Count <> 0 Then
' Fügt das gefundene Element zur Liste der Übereinstimmungen hinzu
lst3.Add(tmpElement1)
' Entfernt das gefundene Element aus der ersten Liste
lst1.RemoveAt(i)
' Entfernt alle gefundenen Elemente aus der zweiten Liste
For Each foundItem As myType In foundItems
lst2.Remove(foundItem)
Next
' Löscht die Liste der gefundenen Elemente für den nächsten Durchlauf
foundItems.Clear()
Else
' Erhöht den Index, wenn kein übereinstimmendes Element gefunden wurde
i = i + 1
End If
Loop Until (i = lst1.Count) ' Fortsetzung der Schleife bis das Ende der ersten Liste erreicht ist
' Gibt die Liste der übereinstimmenden Elemente zurück
Return lst3
End Function
Erläuterung des Codes:
- Diese Funktion ist generisch und kann mit verschiedenen Datentypen (
myType
) verwendet werden. - Sie nimmt zwei Listen (
lst1
undlst2
) als Referenzparameter an, was bedeutet, dass Änderungen an diesen Listen sich auch außerhalb der Funktion widerspiegeln. - Eine neue Liste
lst3
wird erstellt, um alle übereinstimmenden Elemente zu speichern. - Die Funktion durchläuft
lst1
, vergleicht jedes Element mit den Elementen inlst2
, und wenn Übereinstimmungen gefunden werden, werden diese Elemente sowohl auslst1
als auch auslst2
entfernt und inlst3
gespeichert. - Die Schleife wird fortgesetzt, bis das Ende von
lst1
erreicht ist. Beachten Sie, dass der Indexi
nicht erhöht wird, wenn ein Element entfernt wird, da dies die Liste verkürzt und so das Überspringen von Elementen verhindert wird. - Schließlich gibt die Funktion
lst3
zurück, die alle Elemente enthält, die in beiden Listen gefunden und entfernt wurden.
Wenn man davon ausgeht, dass sowohl Liste1 als auch Liste2 Elemente mehrfach enthält, dann könnte man folgendermaßen vorgehen:
' Definiert eine generische Funktion, um zwei Listen zu vergleichen und übereinstimmende Elemente zu finden und aus beiden Listen zu entfernen
Private Function Compare2List_andRemove(Of myType)(ByRef lst1 As List(Of myType), ByRef lst2 As List(Of myType)) As List(Of myType)
' Liste zur Speicherung der übereinstimmenden Elemente
Dim lst3 As New List(Of myType)
' Liste zur temporären Speicherung der gefundenen Elemente
Dim foundItems As New List(Of myType)
' Durchläuft jedes Element in der ersten Liste
For Each element As myType In lst1
' Speichert das aktuelle Element der ersten Liste in einer temporären Variable
Dim tmpElement1 As myType = element
' Findet alle Elemente in der zweiten Liste, die mit dem aktuellen Element der ersten Liste übereinstimmen
foundItems = lst2.FindAll(Function(x As myType) x.ToString = tmpElement1.ToString)
' Überprüft, ob übereinstimmende Elemente gefunden wurden
If foundItems.Count <> 0 Then
' Fügt das gefundene Element zur Liste der Übereinstimmungen hinzu
lst3.Add(tmpElement1)
' Entfernt alle gefundenen Elemente aus der zweiten Liste
For Each foundItem As myType In foundItems
lst2.Remove(foundItem)
Next
' Löscht die Liste der gefundenen Elemente für den nächsten Durchlauf
foundItems.Clear()
End If
Next
' Durchläuft jedes Element in der Liste der Übereinstimmungen
For Each element As myType In lst3
' Speichert das aktuelle Element der Liste der Übereinstimmungen in einer temporären Variable
Dim tmpElement1 As myType = element
' Findet alle Elemente in der ersten Liste, die mit dem aktuellen Element der Liste der Übereinstimmungen übereinstimmen
foundItems = lst1.FindAll(Function(x As myType) x.ToString = tmpElement1.ToString)
' Überprüft, ob übereinstimmende Elemente gefunden wurden
If foundItems.Count <> 0 Then
' Entfernt alle gefundenen Elemente aus der ersten Liste
For Each foundItem As myType In foundItems
lst1.Remove(foundItem)
Next
' Löscht die Liste der gefundenen Elemente für den nächsten Durchlauf
foundItems.Clear()
End If
Next
' Gibt die Liste der übereinstimmenden Elemente zurück
Return lst3
End Function
Erläuterung des Codes:
- Diese Funktion ist generisch und kann für verschiedene Datentypen (
myType
) verwendet werden. - Sie nimmt zwei Listen (
lst1
undlst2
) als Referenzparameter an, sodass Änderungen an diesen Listen sich auch außerhalb der Funktion widerspiegeln. - Eine neue Liste
lst3
wird erstellt, um alle übereinstimmenden Elemente zu speichern. - Zunächst durchläuft die Funktion
lst1
, vergleicht jedes Element mit den Elementen inlst2
, und speichert übereinstimmende Elemente inlst3
. - Dann durchläuft sie
lst3
und entfernt alle Elemente auslst1
, die inlst3
enthalten sind. Dies stellt sicher, dass Elemente, die in beiden Listen vorhanden sind, aus beiden Listen entfernt werden. - Schließlich gibt die Funktion die Liste
lst3
zurück, die alle Elemente enthält, die in beiden Listen gefunden und entfernt wurden.
Duplikate aus Liste entfernen
Selbiges könnte man auch auf eine einzelne List(of T) anwenden, um die Dublikate der Liste zu entfernen.
' Definiert eine generische Subroutine zur Bereinigung einer Liste von Duplikaten
Private Sub ListeBereinigen(Of myType)(ByRef lst As List(Of myType))
' Liste zur Speicherung identifizierter Duplikate
Dim Lst_of_Duplicates As New List(Of myType)
' Durchläuft jedes Element in der übergebenen Liste
For Each element As myType In lst
' Speichert das aktuelle Element in einer temporären Variable
Dim tmpElement As myType = element
' Findet alle Elemente in der Liste, die mit dem aktuellen Element übereinstimmen
Dim foundItems As List(Of myType) = lst.FindAll(Function(x As myType) x.ToString = tmpElement.ToString)
' Überprüft, ob mehr als ein übereinstimmendes Element gefunden wurde (d.h. Duplikate existieren)
If foundItems.Count > 1 Then
' Fügt das gefundene Duplikat zur Liste der Duplikate hinzu
Lst_of_Duplicates.Add(tmpElement)
End If
Next
' Durchläuft jedes Element in der Liste der Duplikate
For Each element As myType In Lst_of_Duplicates
' Speichert das aktuelle Duplikat in einer temporären Variable
Dim tmpElement As myType = element
' Findet alle Elemente in der Original-Liste, die mit dem Duplikat übereinstimmen
Dim foundItems As List(Of myType) = lst.FindAll(Function(x As myType) x.ToString = tmpElement.ToString)
' Überprüft, ob Duplikate gefunden wurden
If foundItems.Count <> 0 Then
' Initialisiert einen Zähler, um das erste Vorkommen des Elements zu überspringen
Dim i As Integer = 0
' Entfernt alle nachfolgenden Vorkommen des Duplikats aus der Liste
For Each foundItem As myType In foundItems
If i <> 0 Then ' Überspringt das erste Vorkommen
lst.Remove(foundItem)
End If
i = i + 1
Next
End If
Next
End Sub
Erläuterung des Codes:
- Diese Prozedur ist generisch und kann für verschiedene Datentypen (
myType
) verwendet werden. - Sie nimmt eine Liste (
lst
) als Referenzparameter an, sodass Änderungen an dieser Liste sich auch außerhalb der Prozedur widerspiegeln. - Die Prozedur identifiziert zunächst alle Duplikate in der Liste und speichert diese in
Lst_of_Duplicates
. - Anschließend durchläuft sie die Liste der Duplikate und entfernt jedes Vorkommen der Duplikate aus der Original-Liste, mit Ausnahme des ersten Vorkommens. Dadurch bleibt jedes Element nur einmal in der Liste erhalten, und alle weiteren Duplikate werden entfernt.
- Diese Art der Duplikatbereinigung ist besonders nützlich, wenn die Elemente der Liste keine einfache Identität wie bei primitiven Datentypen haben, sondern komplexere Objekte sind, deren Gleichheit basierend auf bestimmten Eigenschaften oder Zuständen definiert ist.