Hashset mit List(of /

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Hashset mit List(of /

    Hallo,

    ich habe eine Datenbank mit einer List(of T)

    Diese ist aber mittlerweile dermaßen langsam wenn ich mit z.b. Adressen.Findindex einen Eintrag suche, dass ich gezwungen bin etwas schneller zu werden. Ich würde es gern mit einem Hashset versuchen blicke aber noch nicht ganz durch. Vielleicht kann mir jemand auf die Sprünge helfen.

    Anbei der Code für mich zum ausprobieren:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private adressen As New HashSet(Of Adresseintrag)
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. adressen.Add(New Adresseintrag("Bach1", "Ich", 9))
    5. adressen.Add(New Adresseintrag("Bach2", "Du", 9))
    6. adressen.Add(New Adresseintrag("Bach3", "Er", 9))
    7. adressen.Add(New Adresseintrag("Bach4", "Sie", 9))
    8. adressen.Add(New Adresseintrag("Bach5", "Es", 9))
    9. adressen.Add(New Adresseintrag("Bach6", "Wir", 9))
    10. adressen.Add(New Adresseintrag("Bach7", "Ihr", 9))
    11. adressen.Add(New Adresseintrag("Bach8", "Sie", 9))
    12. End Sub
    13. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    14. Me.ListBox1.Items.Clear()
    15. For a = 0 To adressen.Count - 1
    16. Me.ListBox1.Items.Add(adressen(a).Name1)
    17. Next
    18. Me.ListBox1.Refresh()
    19. End Sub
    20. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    21. ' Suche Name und gebe Adresse aus
    22. ' ??????
    23. End Sub
    24. End Class
    25. <Serializable>
    26. Public Class Adresseintrag
    27. Private adresse As String
    28. Private name As String
    29. Private nummer As Decimal
    30. Public Sub New(adresse As String, name As String, nummer As Decimal)
    31. Me.Adresse1 = adresse
    32. Me.Name1 = name
    33. Me.Nummer1 = nummer
    34. End Sub
    35. Public Property Adresse1 As String
    36. Get
    37. Return adresse
    38. End Get
    39. Set(value As String)
    40. adresse = value
    41. End Set
    42. End Property
    43. Public Property Name1 As String
    44. Get
    45. Return name
    46. End Get
    47. Set(value As String)
    48. name = value
    49. End Set
    50. End Property
    51. Public Property Nummer1 As Decimal
    52. Get
    53. Return nummer
    54. End Get
    55. Set(value As Decimal)
    56. nummer = value
    57. End Set
    58. End Property
    59. End Class


    *Topic verschoben*

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Marcolotti schrieb:

    ich habe eine Datenbank mit einer List(of T)
    Ich sehe in dem Code keinen Hinweis auf eine Datenbank, und ich sehe auch keine List(Of T).
    Ich sehe, dass Zeile#5 nicht funktionieren kann, weil da ein Class1-Dingens geadded wird, wo nur ein Addresseintrag zulässig wäre.

    Übrigens vb.net-Code kann man besser leserlich einstellen, wenn man den VB.net-button benutzt.
    Da hast du recht, wollte es hier in im Thread nur übersichtlicher gestalten. Habe den Beitrag abgeändert.

    Nein, es ist keine reinrassige "Datenbank". Ich verwende in der Produktiven Umgebung eine List<T> Klasse.

    VB.NET-Quellcode

    1. ​Private Adressen1 As New List(Of Adresseintrag)


    und prüfe ob ein Eintrag vorhanden ist:

    VB.NET-Quellcode

    1. ​Dim index As Integer = Adressen1.FindIndex(Function(f) f.Name1.Contains("Er"))
    2. If index > -1 Then
    3. MsgBox("Adresse:" & Adressen1(index).Adresse1)
    4. End If

    Ist aber bei über 6,5 Millionen Adressen und pro Sekunde 200 Abfragen recht langsam.
    Ich würde sagen ein Hashset bzw. für deinen Fall ein Dictionary bring dir hier nix, da du damit nur exakte Übereinstimmungen schnell suchen kannst. Aus deinem Beispiel geht aber hervor, dass du mit ​f.Name1.Contains("Er") auch nur nach Teilen des Strings suchen möchtest. Das kann sehr kompliziert werden. Hier mal ein Artikel dazu:

    High performance “contains” search in list of strings in C#

    Wenn du nicht mit ​Suffix tree anfangen möchtest, kannst du auch erstmal versuchen die liste Parallel zu durchsuchen. Du hast doch bestimmt mehr als 1 Kern. Teil die Liste mit ​Environment.ProcessorCount in gleich große Abschnitte auf und starte für jeden Abschnitt einen Task, der eben nur seinen Teil durchsucht. Dann wartest du mit ​Task.WaitAll darauf, dass alle fertig sind und gibst das gebündelte Ergebnis aus.
    nee - so einen filter Function(f) f.Name1.Contains("Er") - wüsste ich nicht, dassses da einen Weg gäbe, es zu beschleunigen.
    Da muss er ja alle Addressen durchgehen, und in jede einzelne reingucken.

    bei Function(f) f.Name1.StartsWith("Er") hätte man was machen können, indem man die Addressen nach Name1 sortiert, und dann eine binäre suche ausführt.

    Bluespide schrieb:

    Ich würde sagen ein Hashset bzw. für deinen Fall ein Dictionary bring dir hier nix, da du damit nur exakte Übereinstimmungen schnell suchen kannst. Aus deinem Beispiel geht aber hervor, dass du mit ​f.Name1.Contains("Er") auch nur nach Teilen des Strings suchen möchtest. Das kann sehr kompliziert werden. Hier mal ein


    Die Adresse ist ein einmaliger Hexwert mit 34 Zeichen. Der kommt nur einmal vor.
    Achso, dann ca. so:

    VB.NET-Quellcode

    1. Private adressen As New Dictionary(Of String, Adresseintrag)
    2. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    3. adressen.Add("Ich", New Adresseintrag("Bach1", "Ich", 9))
    4. adressen.Add("Du", New Adresseintrag("Bach2", "Du", 9))
    5. adressen.Add("Er", New Adresseintrag("Bach3", "Er", 9))
    6. adressen.Add("Sie", New Adresseintrag("Bach4", "Sie", 9))
    7. adressen.Add("Es", New Adresseintrag("Bach5", "Es", 9))
    8. adressen.Add("Wir", New Adresseintrag("Bach6", "Wir", 9))
    9. adressen.Add("Ihr", New Adresseintrag("Bach7", "Ihr", 9))
    10. End Sub
    11. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    12. Dim stringToFind As String = "Er"
    13. If adressen.ContainsKey(stringToFind) Then
    14. MsgBox("Adresse:" & adressen(stringToFind).Adresse1)
    15. End If
    16. End Sub


    //Edit: Aber warte. Du suchst ja nach dem Name1 und nicht der Adresse1. ​Adresse1 möchtest du ja raus bekommen. Dictionary funktioniert nur, wenn der Suchbegriff einmal ist und nach diesem exakt gesucht wird.
    Hallo.

    Habe die ganze Anwendung auf Dictionarys umgestellt. Es läuft sehr viel schneller. :thumbsup:

    ABER:

    Ich habe folgendes Problem. Wenn ich das Dictionary nach einem Shutdown von der Platte wieder laden möchte, gibt es einen Fehler:

    "System.ArgumentException: An item with the same key has already been added."

    Da aber jeder Account einmalig ist und ich vor jedem Anlegen eines Accounts mit containskey prüfe ob er schon vorhanden ist, kann ich mir den Fehler nicht erklären. Das gespeicherte Dictionary ist 1,5GB groß. Das speichern klappt ohne Probleme.

    Hier der Code für´s speichern.

    VB.NET-Quellcode

    1. ​Public Function ArraySave(ByVal Filename As String,
    2. ByVal arr As Object) As Boolean
    3. Dim fs As FileStream = Nothing
    4. Dim Success As Boolean = False
    5. Try
    6. ' Datei zum Schreiben öffnen
    7. fs = New FileStream(Filename, FileMode.Create, FileAccess.Write)
    8. ' Array serialisieren und speichern
    9. Dim formatter As New BinaryFormatter()
    10. formatter.Serialize(fs, arr)
    11. Success = True
    12. Catch ex As Exception
    13. Console.WriteLine(DateTime.Now & vbCrLf & Filename & vbCrLf & ex.ToString)
    14. Return False
    15. Finally
    16. ' Datei schließen
    17. If Not IsNothing(fs) Then fs.Close()
    18. End Try
    19. Return (Success)
    20. End Function


    Der Code fürs Laden:

    VB.NET-Quellcode

    1. ​Public Function ArrayRead(ByVal Filename As String,
    2. ByRef arr As Object) As Boolean
    3. Dim Success As Boolean = False
    4. ' Prüfen, ob Datei existiert
    5. If IO.File.Exists(Filename) Then
    6. Dim fs As FileStream = Nothing
    7. Try
    8. ' Datei zum Lesen öffnen
    9. fs = New FileStream(Filename, FileMode.Open, FileAccess.Read)
    10. ' Daten deserialiseren und dem Array zuweisen
    11. Dim formatter As New BinaryFormatter()
    12. arr = formatter.Deserialize(fs)
    13. Success = True
    14. Catch ex As Exception
    15. Console.WriteLine("Array-Read: " & ex.ToString)
    16. Finally
    17. ' Datei schließen
    18. If Not IsNothing(fs) Then fs.Close()
    19. End Try
    20. End If
    21. Return (Success)
    22. End Function


    Hat jemand eine Idee?

    2. Problem ist:

    Mit den Listen List(of) konnte ich zu festen Zeitpunkten Snapshots machen. Ich habe dazu einem separaten Thread die Liste übergeben und dieser konnte sich austoben.

    Das gleiche habe ich mit dem Dictionary versucht aber folgender Fehler: "System.InvalidOperationException: Collection was modified; enumeration operation may not execute."
    Ich hab dein Code ausprobiert - bei mir geht das:

    VB.NET-Quellcode

    1. Public Shared Function ArraySave(ByVal Filename As String, ByVal arr As Object) As Boolean
    2. Dim Success As Boolean = False
    3. ' Datei zum Schreiben öffnen
    4. Using fs = New FileStream(Filename, FileMode.Create, FileAccess.Write)
    5. ' Array serialisieren und speichern
    6. Dim formatter As New BinaryFormatter()
    7. formatter.Serialize(fs, arr)
    8. Success = True
    9. End Using
    10. Return (Success)
    11. End Function
    12. Public Shared Function ArrayRead(ByVal Filename As String, ByRef arr As Object) As Boolean
    13. Dim Success As Boolean = False
    14. If IO.File.Exists(Filename) Then
    15. Using fs = New FileStream(Filename, FileMode.Open, FileAccess.Read)
    16. ' Daten deserialiseren und dem Array zuweisen
    17. Dim formatter As New BinaryFormatter()
    18. arr = formatter.Deserialize(fs)
    19. Success = True
    20. End Using
    21. End If
    22. Return (Success)
    23. End Function
    24. Public Shared Sub Main(ParamArray args() As String)
    25. Dim dic As New Dictionary(Of String, String) From {{"key0", "8989"}, {"key1", "8989"}, {"key2", "8989"}, {"key3", "8989"}, {"key4", "8989"}}
    26. Dim file = Path.GetFullPath("..\..\myDic.bin")
    27. ArraySave(file, dic)
    28. Dim dic2 As Dictionary(Of String, String) = Nothing
    29. ArrayRead(file, dic2)
    (die TryCatches und das vb6-Zeugs habich entfernt, damit ich debuggen kann, und es bei mir kompiliert).
    Ansonsten: Vermutlich liest du eine falsche Datei ein.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Müsste eigentlich.
    Habich allerdings nicht ausprobiert, und ich werd jetzt auch kein son Riesen-Ding generieren.
    Vielleicht tritt bei dieser Datenmenge auch mal ein Hash-Verwechsler auf.
    Also dass zwei verschiedene Keys dann doch denselben Hashwert erzeugen.
    Du könntest dir eine andere Art der Speicherung überlegen.
    Man könnte probieren, die Einträge zeilenweise in einen GZip-Stream zu schreiben.
    Sollte dann auch eine kompaktere Datei ergeben.
    Und beim Laden könnte man die Dublette zu fassen kriegen - wenn da die entsprechende Exception auftritt.