VB.NET 2008 DOループ中にアプリケーションがクラッシュします
質問
ListBox1の各アイテムをListBox2のすべてのアイテムと比較するアプリケーションを作成しています。アイテムが見つかった場合は、両方のリストから削除します。目標は、見つかっていないアイテムのみが両方のリストに残ることです。
問題は、アプリケーションがハングするだけで、結果が得られないことです。私は自分のコードを数回見ましたが、何が起こっているのかを理解できません(私が知っているnoobをプログラミングします...)。
誰かがこれを手伝ってくれますか?
コードスニペット:
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
解決
listbox1.items.countがlistbox2.items.count -1である場合、xはlistbox1.items.countに等しくないので、exitは実行されず、コードは単に際限なくループします。
Do While y = 1
たとえば、リスト管理を容易にするためにLINQを使用することを検討しましたか?
編集:さらに、各削除がループカウンターをオフセットするため、for(それぞれを使用してそれを行うことはまったく違法です)で横断するリストからアイテムを削除することは間違っています。
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 part)コピーは必要な部分です。そうでなければ、デュープはリストボックスのアイテムのサブセットにすぎないため、再び靴ひもを引っ張って自分自身を持ち上げようとします。
最後の部分は、単純な削除ループで、コレクションからアイテムを削除します
他のヒント
あなたが持っている
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番目のポイントで言及されているように、元の時間をスキップした要素をカバーするためにそこにループします。
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
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とEratosthenesのふるいで悪い経験をしました。