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循环放在那里是为了覆盖上一次跳过的元素,正如我在第三点中提到的那样。

于 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 的项目的一个子集,我们将再次试图通过拉紧我们的鞋带来提升自己。

最后一部分只是一个简单的删除循环,它从集合中删除项目

于 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

我对三种不同的方法进行了测试。他们是 Joels、sweko 和我的。我这样做是为了测试性能,但我发现结果不一样,列表框不一样。这是我用来测试的代码,所以你可以做判断。可能是我的一些愚蠢的错误。

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 回答