填充时,VB.NET对象数组会变慢

时间:2013-07-05 17:27:55

标签: arrays vb.net performance

我在VB.NET中遇到了问题。我有一个自定义对象数组,“ITNObject”,它有两个元素,一个sting和一个int。

代码循环遍历一组XML文件,读取节点,然后添加到主阵列。目的是然后将数组输出到MySQL表。

现在,重要的一点。当我在每个循环结束时写入数组(每个文件一次),性能是线性的。

但是,如果我为所有文件填充一个数组,并在结束时写一次,那么随着数组填充,应用程序会逐渐变慢。

最后阵列中有数百万个对象。

有什么建议吗?下面的代码显示了数组填充的部分,为每个文件调用此代码。在这个例子中,它填充了整个文件集。

减速很重要,每个文件开始大约需要15毫秒,经过几百个循环(阵列中大约100,000个ITNObjects)后,每个文件减慢到大约600毫秒。

我该如何解决这个问题?

Public Sub ReadITN(filetoDecompress As String)

    Dim XDoc As Xml.XmlDocument
    XDoc = New Xml.XmlDocument
    XDoc.Load(filetoDecompress)
    Dim nsmgr = New XmlNamespaceManager(XDoc.NameTable)
    nsmgr.AddNamespace("os", "http://www.ordnancesurvey.co.uk/xml/namespaces/osgb")

    Dim i As Integer
    Dim RoadRank As Integer
    Dim toid() As String
    Dim x As Integer
    Dim descNode As XmlNode
    Dim xmlNodes As XmlNodeList = XDoc.SelectNodes("//os:RoadLink", nsmgr)
    Dim loopCount As Integer = 0
    For Each mxmlnode As XmlNode In xmlNodes 'XDoc.GetElementsByTagName("osgb:RoadLink")
        x = 0
        ReDim Preserve toid(x)

        For Each n As XmlNode In mxmlnode.SelectNodes(".//os:referenceToTopographicArea", nsmgr)
            'If n.Name = "osgb:referenceToTopographicArea" Then
            ReDim Preserve toid(x)
            toid(x) = Microsoft.VisualBasic.Right(n.Attributes.Item(0).Value, Len(n.Attributes.Item(0).Value) - 1)
            x = x + 1
            'End If
        Next n

        descNode = mxmlnode.SelectSingleNode(".//os:descriptiveTerm", nsmgr)

        RoadRank = 0
        Select Case UCase(descNode.InnerText)
            Case "MOTORWAY" : RoadRank = 1
            Case "A ROAD" : RoadRank = 2
            Case "B ROAD" : RoadRank = 3
            Case "MINOR ROAD" : RoadRank = 4
            Case "LOCAL STREET" : RoadRank = 5
            Case "PRIVATE ROAD" : RoadRank = 6
            Case "PRIVATE ROAD - RESTRICTED ACCESS" : RoadRank = 7
            Case "PRIVATE ROAD - PUBLICLY ACCESSIBLE" : RoadRank = 8
            Case "PEDESTRIANISED STREET" : RoadRank = 9
            Case "ALLEY" : RoadRank = 10
        End Select

        If toid(0) <> "" Then

            For i = 0 To x - 1
                ReDim Preserve ITNObjects(ITNCount)
                If ITNObjects(ITNCount) Is Nothing Then
                    ITNObjects(ITNCount) = New ITNObject()
                End If

                ITNObjects(ITNCount).Toid = toid(i)
                ITNObjects(ITNCount).RoadRank = RoadRank
                ITNCount = ITNCount + 1
            Next i
        End If
        Erase toid
    Next

    descNode = Nothing
    xmlNodes = Nothing
    XDoc = Nothing
    toid = Nothing
End Sub

2 个答案:

答案 0 :(得分:3)

使用列表而不是数组。

在.NET中,您无法调整数组大小。 ReDim Preserve命令创建一个新数组,并将旧数组中的所有项复制到新数组。数组越大,这自然会花费更长的时间。

创建列表:

Dim ITNObjects as New List(Of ITNObject)

将项目添加到列表中:

Dim temp as New ITNObject()
temp.Toid = toid(i)
temp.RoadRank = RoadRank
ITNObjects.Add(temp)

答案 1 :(得分:2)

  ReDim Preserve toid(x)

你写了一个O(n ^ 2)算法。每次向数组添加新元素时,您都要创建一个需要复制所有旧元素的 new 数组。正如你所发现的那样,O(n ^ 2)算法对n的大值表现不佳。

您可以使用所谓的指数退避分配策略解决此问题。原理很简单,只需将数组的大小加倍即可。像这样:

 If toid.GetUpperBound(0) < x Then ReDim Preserve toid(2 * x)

.NET集合类使用的策略完全相同。像List(Of T),更好的数组类型。