Die Community zu .NET und Classic VB.
Menü

VB.NET-Tipp 0080: Suchen und Sortieren mit dem .NET-Framework

 von 

Beschreibung

Dank Schnittstelle und Delegaten braucht man in Visual Basic .NET die eigentlichen Such- bzw. Sortieralgorithmen nicht mehr selbst zu programmieren. Man muss nur definieren, wie 2 Objekte zu vergleichen sind, bzw. wann eine Suche einen Treffer hat, und kann dann die effizienten Algorithmen von Array oder der generischen List-Klasse für sich arbeiten lassen.
Im Beispiel wird ein fiktives Inhaltsverzeichnis ( "1.2.3 Text" etc. ) bearbeitet.

Schwierigkeitsgrad:

Schwierigkeitsgrad 1

Framework-Version(en):

.NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5

.NET-Version(en):

Visual Basic 2005, Visual Basic 2008

Download:

Download des Beispielprojektes [14,18 KB]

' Dieser Quellcode stammt von http://www.activevb.de
' und kann frei verwendet werden. Für eventuelle Schäden
' wird nicht gehaftet.

' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum.
' Ansonsten viel Spaß und Erfolg mit diesem Source!

' Projektversion:   Visual Studio 2005
' Option Strict:    An
'
' Referenzen: 
'  - System
'  - System.Data
'  - System.Deployment
'  - System.Drawing
'  - System.Web.Services
'  - System.Windows.Forms
'  - System.Xml
'
' Imports: 
'  - Microsoft.VisualBasic
'  - Microsoft.VisualBasic.ControlChars
'  - System
'  - System.Collections
'  - System.Collections.Generic
'  - System.Data
'  - System.Drawing
'  - System.Diagnostics
'  - System.Windows.Forms
'

' ##############################################################################
' ############################ frmSortAndSearch.vb #############################
' ##############################################################################
Public Class frmSortAndSearch
    Private _Data As New List(Of String)
    Private _Comparer As New NumericStringComparer

    Private Sub frmSortAndSearch_Load(ByVal sender As Object, _
        ByVal e As EventArgs) Handles MyBase.Load

        ' Zum Aufzeigen der Eigenheiten lexikalischer / numerischer Sortierung 
        ' ist der Ziffernbereich von 4-8 unwesentlich
        Dim Numbs As Integer() = New Integer() {1, 2, 3, 9, 10, 11}
        For Each I As Integer In Numbs
            _Data.Add(String.Concat(I, " Text"))
            For Each II As Integer In Numbs
                _Data.Add(String.Concat(I, ".", II, " Text"))
                For Each III As Integer In Numbs
                    _Data.Add(String.Concat(I, ".", II, ".", III, " Text"))
                Next
            Next
        Next
        Me.btUnSort.PerformClick()
    End Sub

    Private Sub SortOptions_Click(ByVal sender As Object, _
            ByVal e As EventArgs) Handles btUnSort.Click, btLexicalic.Click, _
            btNumeric.Click, btComparisonNumeric.Click

        Select Case True
            Case sender Is btUnSort
                Shuffle(_Data)

            Case sender Is btLexicalic
                ' (für Inhaltsverzeichnisse unzureichende) Standard-Sortierung
                _Data.Sort()

            Case sender Is btNumeric
                ' benutzerdefinierte Sortierung mittels IComparer 
                ' implementierender Vergleicher-Klasse
                _Data.Sort(_Comparer)

            Case sender Is btComparisonNumeric
                ' benutzerdefinierte Sortierung mittels 
                ' Vergleicher(-Funktion(Comparison - Delegat))
                _Data.Sort(AddressOf modHelpers.NumericStringComparison)

        End Select
        Me.ListBox1.DataSource = Nothing
        Me.ListBox1.DataSource = _Data
        Me.ListBox1.SelectedIndices.Clear()
    End Sub

    Private Sub Search_Click(ByVal sender As Object, _
        ByVal e As System.EventArgs) _
        Handles btByMatch.Click, btBinarySearch.Click

        Me.ListBox1.SelectedIndices.Clear()
        Select Case True
            Case sender Is btByMatch
                ' Findet nur exakte Übereinstimmungen
                Me.ListBox1.SelectedIndex = _
                    _Data.FindIndex(AddressOf SearchPredicate)

            Case sender Is btBinarySearch
                ' Findet auch die Werte, die dem Suchbegriff am Nächsten kommen
                FindBinary()

        End Select
    End Sub

    Private Sub Shuffle(ByVal Lst As System.Collections.IList)
        Static Rnd As New Random()
        For I As Integer = 1 To Lst.Count - 1
            ' Lst(I) mit einer zufällig aus dem bisher durchlaufenem 
            ' Bereich gewählten vertauschen
            Dim II As Integer = Rnd.Next(I + 1)
            Dim Tmp As Object = Lst(I)
            Lst(I) = Lst(II)
            Lst(II) = Tmp
        Next
    End Sub

    ''' <summary>
    ''' Diese Function passt auf den im Framework vorgefertigten 
    ''' generischen Delegaten Predicate(Of T), und kann daher bei 
    ''' List(Of T).FindIndex() als Predicate angegeben werden
    ''' </summary>
    Private Function SearchPredicate(ByVal S As String) As Boolean
        Return S.Substring(0, S.IndexOf(" ")) = Me.TextBox1.Text
    End Function

    Private Sub FindBinary()
        ' während List(Of T).Sort() u.a. Comparisons akzeptiert, 
        ' bleibt BinarySearch() auf die IComparer-Schnittstelle 
        ' angewiesen. Warum?
        Dim Index As Integer = _Data.BinarySearch(Me.TextBox1.Text, _Comparer)

        ' Ein negativer Index ist das Bit-Komplement der fiktiven 
        ' **Einsortierposition**, wenn eine exakte Übereinstimmung 
        ' nicht gefunden wurde.
        Select Case Index
            Case Is >= 0
                Me.ListBox1.SelectedIndex = Index

            Case -1 ' kleiner als alle
                Me.ListBox1.SelectedIndex = 0

            Case -_Data.Count ' größer als alle
                Me.ListBox1.SelectedIndex = _Data.Count - 1

            Case Else ' 2 Item markieren die Einsortierposition
                Index = Index Xor -1
                Me.ListBox1.SelectedIndices.Add(Index)
                Me.ListBox1.SelectedIndices.Add(Index - 1)

        End Select
    End Sub
End Class


' ##############################################################################
' ################################ Helpers.vb ##################################
' ##############################################################################
Public Module modHelpers

    ''' <summary>
    ''' konvertiert etwa "2.11.3 Blabla" nach Integer(){2, 11, 3}
    ''' </summary>
    Private Function StringToNumbs(ByVal S As String) As Integer()
        Dim SpaceIndex As Integer = S.IndexOf(" ")
        If SpaceIndex < 0 Then SpaceIndex = S.Length
        Dim Splitted() As String = S.Substring(0, SpaceIndex).Split("."c)
        Dim RetVal(Splitted.Length - 1) As Integer

        For I As Integer = 0 To Splitted.Length - 1
            RetVal(I) = Integer.Parse(Splitted(I))
        Next
        Return RetVal
    End Function

    ''' <summary>
    ''' Diese Function passt auf den im Framework vorgefertigten 
    ''' generischen Delegaten Comparison(Of T), und kann daher bei 
    ''' List(Of T).Sort() als Comparison angegeben werden
    ''' </summary>
    Public Function NumericStringComparison(ByVal x As String, _
         ByVal y As String) As Integer

        Dim NumbsX As Integer() = StringToNumbs(x)
        Dim NumbsY As Integer() = StringToNumbs(y)
        For I As Integer = 0 To Math.Min(NumbsX.Length, NumbsY.Length) - 1
            Dim C As Integer = NumbsX(I).CompareTo(NumbsY(I))
            If C <> 0 Then Return C
        Next

        ' alle verglichenen Elemente waren gleich
        ' also den Vergleich der Länge der beiden Arrays zurückgeben
        Return NumbsX.Length.CompareTo(NumbsY.Length)
    End Function

End Module
' ##############################################################################
' ######################### NumericStringComparer.vb ###########################
' ##############################################################################
' Umständlichkeit der IComparer-Schnittstelle - für Sortierung überflüssig, da 
' man die Comparison auch direkt angeben kann, für die binäre Suche bleibt sie 
' leider erforderlich.
Public Class NumericStringComparer : Implements IComparer(Of String)
    Public Function Compare(ByVal x As String, ByVal y As String) As Integer _
          Implements System.Collections.Generic.IComparer(Of String).Compare
        Return modHelpers.NumericStringComparison(x, y)
    End Function
End Class

Ihre Meinung  

Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.