代码之家  ›  专栏  ›  技术社区  ›  Simon

慢选择单节点

  •  2
  • Simon  · 技术社区  · 16 年前

    我有一个简单的结构化XML文件,如下所示:

    <ttest ID="ttest00001", NickName="map00001"/>
    <ttest ID="ttest00002", NickName="map00002"/>
    <ttest ID="ttest00003", NickName="map00003"/>
    <ttest ID="ttest00004", NickName="map00004"/>
    

    … 这个XML文件大约为2.5MB。

    在我的源代码中,我将有一个循环来获取昵称

    在每个循环中,我都有这样的东西:

    nickNameLoopNum = MyXmlDoc.SelectSingleNode("//ttest[@ID=' + testloopNum + "']").Attributes["NickName"].Value
    

    这条线要花我30到40毫秒。

    我搜索了一些老文章(可以追溯到2002年)说,使用某种编译过的“xpath”可以帮助解决这个问题,但那是5年前的事了。我想知道有没有一个现代化的做法,使它更快?(我正在使用.NET 3.5)

    4 回复  |  直到 15 年前
        1
  •  4
  •   Dimitre Novatchev    16 年前

    使用“ // “xpath表达式中的缩写会导致很大的效率低下 因为它会导致搜索整个xml文档。使用 / / “不断地增加这种低效率。

    一个有效的解决方案 问题是获得所有 NickName “只计算一个xpath表达式的属性节点:

    ttest/@NickName

    其中上下文节点是所有 ttest “元素”。

    C码 将如下所示:

        int n = 15;
        XmlDocument doc = new XmlDocument();
        doc.Load("MyFile.xml");
    
        XmlNodeList nodeList;
        XmlNode top = doc.DocumentElement;
        nodeList =
            top.SelectNodes("ttest/@NickName");
    
        // Get the N-th NickName, can be done in a loop for
        // all n in a range
    
        string nickName = nodeList[n].Value;
    

    在这里,我们假设“ttest”元素是xml文档顶部元素的子元素。

    总结 提出了一种有效的解决方案,它只对xpath表达式求值一次,并将所有结果放入一个方便的ienumerable对象(可以用作数组)中,以访问 O(c) 时间。

        2
  •  3
  •   marcus.greasly    16 年前

    您已经在使用xpath(“//ttest…”),这是访问doc节点最慢的方式,因为“/”语法可以在整个doc中查找。

    试试……

    foreach (XMLNode node in MyXmlDoc.ChildNodes) {
        ...
    }
    

    相反,不需要xpath,而且应该更快。(隐式假设它是一个没有嵌套的“平面”xml文件。如果是的话,你很快就会复发的。

        3
  •  1
  •   argatxa    15 年前

    回答迪米特

    事实上。。。选择整个节点比只选择属性要快。

    我有一个单元测试基准测试下面的代码,并且(令人惊讶地)选择完整节点和处理属性比选择属性和直接获取值更快。

    将其放入10000个迭代循环中,并交换注释以进行各种测试。

     //XmlNodeList nodeList = document.SelectNodes("test/@NickName");
                XmlNodeList nodeList = document.SelectNodes("test");
                foreach (XmlNode node in nodeList)
                {
                    //string nickName = node.Value;
                    string nickName = ((XmlAttribute)node.Attributes.GetNamedItem("NickName")).Value;
    
                }

    我知道这是违反直觉的,但是…你必须测量!!

        4
  •  0
  •   Eric Rosenberger    16 年前

    在这种情况下,您可能需要考虑将xml文件中的昵称读入一个数组(如果您的测试id实际上只是顺序整数)或一个字典(如果不是),然后使用它来定位每个昵称,而不是尝试执行一堆xpath查询。这样你在查找时可能会得到更好的性能。

    编辑:类似这样(伪代码)

    var nicknames = new Dictionary<string, string>();
    
    foreach (XmlNode node in MyXmlDoc.ChildNodes)
    {
        if (node is XmlElement)
        {
            nicknames.Add(node.Attributes["ID"], node.Attributes["NickName"]);
        }
    }
    
    ...
    
    nickNameLoopNum = nicknames[testLoopNum];