0

listbox1 の各項目を listbox2 のすべての項目と比較するアプリケーションを作成しています。アイテムが見つかった場合は、両方のリストから削除します。目標は、見つからなかったアイテムのみを両方のリストに残すことです。

問題は、アプリケーションがハングするだけで、結果が得られないことです。私は自分のコードを数回見ましたが、何が起こっているのかわかりません (私が知っているプログラミング初心者...)。

誰でもこれで私を助けることができますか?

コードスニペット:

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

    Dim a As String
    Dim b As String
    Dim y As String

    For i As Integer = 0 To ListBox1.Items.Count - 1
        a = ListBox1.Items(i)
        y = 1
        Do While y = 1
            For x As Integer = 0 To ListBox2.Items.Count - 1
                b = ListBox2.Items(x)
                Dim res As Int16 = String.Compare(a, b)
                If res = 0 Then
                    y = 0
                    ListBox2.Items.Remove(i)
                    ListBox2.Items.Remove(x)
                ElseIf x = ListBox1.Items.Count Then
                    Exit Do
                End If
            Next
        Loop
    Next
End Sub
4

4 に答える 4

2

あなたが持っている

ElseIf x = ListBox1.Items.Count Then
    Exit Do

いつあるべきか

ElseIf x = ListBox1.Items.Count - 1 Then
    Exit Do

forループはXをカウントに変更し、その値で反復せずに終了するためです。

それだけでなく、なぜDoとにかくループがあるのですか?同じ内部リストボックスを繰り返して重複を探す必要はありませんね。

そして第三に、あなたがそれらを反復している間、あなたは物を取り除くべきではありません。あなたの場合、forループはカウントを再利用しているので「安全」ですが、削除操作はインデックスを再作成するので、削除するときにiとxのイテレータから1を引いて、次のイテレータがインデックスの再作成によってスキップされないようにする必要があります。

考え直してみるとDo、私の3番目のポイントで述べたように、前回スキップした要素をカバーするために、そのループをそこに配置することができます。

于 2010-06-04T20:10:06.483 に答える
1

ListBox1.Items.CountがListBox2.Items.Count-1より大きい場合、XはListBox1.Items.Countと等しくなることはないため、Exit Doは実行されず、コードは

Do While y = 1

たとえば、リスト管理を容易にするためにLinqを使用することを検討しましたか?

編集:さらに、削除するたびにループカウンターがオフセットされるため、forを使用してトラバースしているリストからアイテムを削除することは誤りです(For Eachを使用して削除することはまったく違法です)。

edit2:タスクを実行するLinqスニペットは次のとおりです。

    Dim itemsFirst = (From item As String In ListBox1.Items Select item)
    Dim itemsSecond = (From item As String In ListBox2.Items Select item)

    Dim dupes = System.Linq.Enumerable.Intersect(itemsFirst, itemsSecond).ToList
    For Each item In dupes
        ListBox1.Items.Remove(item)
        ListBox2.Items.Remove(item)
    Next item

基本的には、両方のリストから文字列を抽出します(これは、ListBox.Itemsコレクションが少し奇妙なために必要です)

その後、intersectメソッドを実行し、結果をリストにコピーします。(.ToList部分)コピーは必須の部分です。そうしないと、重複がListBoxのItemsのサブセットになり、もう一度、靴ひもを引っ張って自分自身を持ち上げようとするためです。

最後の部分は、コレクションからアイテムを削除する単純な削除ループです。

于 2010-06-04T20:09:35.440 に答える
1

Visual Studio 2008 以降を使用している場合:

Dim dupes = Listbox1.Items.Cast(Of String)().Intersect(Listbox2.Items.Cast(Of String)()).ToArray() 
For Each item As String in dupes
    Listbox1.Items.Remove(item)
    Listbox2.Items.Remove(item)
Next item
于 2010-06-04T20:14:32.040 に答える
0

私は3つの異なる方法のテストを実行しました。彼らはジョエル、スウェコ、そして私のものです。パフォーマンスをテストするためにこれを行っていましたが、結果が同じではなく、リストボックスが同じではないことがわかりまし。これが私がテストに使用したコードですので、あなたが裁判官になることができます。おそらく私の側のいくつかのばかげた間違い。

Dim stpw As New Stopwatch

Private Sub Button1_Click(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) Handles Button1.Click
    Debug.WriteLine("")
    loadLBTD() ''#load test data

    doSTPW("Test 1 Start", False) ''#mine
    ''#get rid of dupes <<<<<<<<<<<<<<
    Dim dupeL As New List(Of String)
    For x As Integer = ListBox1.Items.Count - 1 To 0 Step -1
        If ListBox2.Items.Contains(ListBox1.Items(x)) Then
            dupeL.Add(ListBox1.Items(x))
            ListBox1.Items.RemoveAt(x)
        End If
    Next
    For Each s As String In dupeL
        ListBox2.Items.Remove(s)
    Next
    doSTPW("Test 1 End")

    loadLBTD() ''#load test data

    doSTPW("Test 2 Start", False) ''#sweko
    ''#get rid of dupes <<<<<<<<<<<<<<
    ''#I had to set Option Strict to Off to get this to work <<<<<<<
    Dim itemsFirst = (From item As String In ListBox1.Items Select item)
    Dim itemsSecond = (From item As String In ListBox2.Items Select item)

    Dim dupes = System.Linq.Enumerable.Intersect(itemsFirst, itemsSecond).ToList
    For Each item In dupes
        ListBox1.Items.Remove(item)
        ListBox2.Items.Remove(item)
    Next item
    doSTPW("Test 2 End")

    loadLBTD() ''#load test data

    doSTPW("Test 3 Start", False) ''#joel
    ''#get rid of dupes <<<<<<<<<<<<<<
    Dim dupes2 = ListBox1.Items.Cast(Of String)().Intersect(ListBox2.Items.Cast(Of String)()).ToArray()
    For Each item As String In dupes2
        ListBox1.Items.Remove(item)
        ListBox2.Items.Remove(item)
    Next item
    doSTPW("Test 3 End")
End Sub

Private Sub doSTPW(ByVal someText As String, Optional ByVal showTM As Boolean = True)
    stpw.Stop() ''#stop the clock
    If flip Then Debug.Write("'T ") Else Debug.Write("'F ")
    Debug.Write("LBCnts " & ListBox1.Items.Count & " " & ListBox2.Items.Count)
    Dim s As String
    If showTM Then
        s = String.Format("  {0}  {1}", someText, stpw.ElapsedTicks.ToString("N0"))
    Else
        s = String.Format("  {0}", someText)
    End If
    Debug.WriteLine(s)
    stpw.Reset() ''#reset and start clock
    stpw.Start()
End Sub

Dim flip As Boolean = False
Private Sub loadLBTD()
    ''#Create test data 
    Dim tl1() As String = New String() {"A", "X", "y", "z", "B", "w", "X", "y", "z"}
    Dim tl2() As String = New String() {"A", "y", "z", "Q", "A", "y", "z", "Q", "A", "y", "z", "Q"}
    ListBox1.Items.Clear()
    ListBox2.Items.Clear()
    ''#load listboxes
    If flip Then
        ListBox1.Items.AddRange(tl2)
        ListBox2.Items.AddRange(tl1)
    Else
        ListBox1.Items.AddRange(tl1)
        ListBox2.Items.AddRange(tl2)
    End If
    ''#end of test data setup
End Sub

また、予想どおり、LINQはより簡潔ですが、低速です。コードの使用頻度が低い場合は問題ありません。私はLINQとエラトステネスのふるいで悪い経験をしました。

于 2010-06-05T11:45:37.637 に答える