1

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

代码循环一组 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
4

2 回答 2

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)
于 2013-07-05T17:32:34.827 回答
2
  ReDim Preserve toid(x)

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

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

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

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

于 2013-07-05T17:43:27.850 回答