XPath:NodeList反復の高速化

それほど小さくない通常のXMLファイル(実際には約1,000レコードのみ)を処理しようとしたとき、XPathを使用した抽出に伴うNodeListの繰り返しが大幅に遅くなり始め(ファイルで約2分かかります)、処理とともにブレーキが増加することがわかりました後続の各ノード(ノード)。 この問題も発生します。

blog.astradele.com/2006/02/24/slow-xpath-evaluation-for-large-xml-documents-in-java-15

jbwhammie.blogspot.com/2011/02/make-java-xpath-work-on-large-files.html

私が理解しているように、これはJDKのDOMおよびXPath実装の機能です。XPathリクエストから特定のXMLノードを取得した場合、このノードは親を介して親XML文書に参照されたままになります。 そして、このノードに対する後続のXPath要求は、ノード自体だけでなくXMLドキュメント全体が処理されるという事実につながります。 したがって、たとえば、このノードをその親ノードから削除することにより、親をノードにリセットしようとすることは論理的です。 上記のリンクで提供されているのはこの方法であり、機能します。 唯一の欠点は、XMLドキュメント自体を変更し、それ以上のデータ抽出に適さないことです。 ドキュメントによれば、クローンにはparent = nullあるため、XMLドキュメント自体を変更しない別の方法があることがわかりました。

実際に、説明されている問題とソリューションへのアプローチは、次のコードで説明されています。

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;

public class SlowXPath {
public static void main( String [] args) throws Exception {
final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

String xmlString = prepareXml(5000);

// System.out.println(xmlString);
final Document xmlDoc = documentBuilder.parse( new ByteArrayInputStream(xmlString.getBytes()));

final XPathFactory xPathFactory = XPathFactory.newInstance();

final XPathExpression nodeXPath = xPathFactory.newXPath().compile( "//node" );
final XPathExpression iXPath = xPathFactory.newXPath().compile( "./i/text()" );

final NodeList nodeList = (NodeList) nodeXPath.evaluate(xmlDoc, XPathConstants.NODESET);

System. out .println( "Nodes number=" + nodeList.getLength());

timeIt( "Simple iterate" , new Runnable() {
@Override
public void run() {
int sum = 0;

for ( int i = 0; i < nodeList.getLength(); i++) {
final Node node = nodeList.item(i);
try {
final String iStr = ( String ) iXPath.evaluate(node, XPathConstants.STRING);
sum += Integer.parseInt(iStr.trim());
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}

System. out .println( "Sum=" + sum);
}
});
timeIt( "Iterate with cloning" , new Runnable() {
@Override
public void run() {
int sum = 0;

for ( int i = 0; i < nodeList.getLength(); i++) {
final Node node = nodeList.item(i).cloneNode( true ); // <-- Note cloning here
try {
final String iStr = ( String ) iXPath.evaluate(node, XPathConstants.STRING);
sum += Integer.parseInt(iStr.trim());
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}

System. out .println( "Sum=" + sum);
}
});
timeIt( "Iterate with detaching node" , new Runnable() {
@Override
public void run() {
int sum = 0;

for ( int i = 0; i < nodeList.getLength(); i++) {
final Node node = nodeList.item(i);

node.getParentNode().removeChild(node); // <-- Note detaching node

try {
final String iStr = ( String ) iXPath.evaluate(node, XPathConstants.STRING);
sum += Integer.parseInt(iStr.trim());
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}

System. out .println( "Sum=" + sum);
}
});
}

private static String prepareXml( int n) {
StringBuilder sb = new StringBuilder ();

sb.append( "<root>" );

for ( int i = 0; i < n; i++) {
sb.append( "<node><i>\n" )
.append(i)
.append( "</i></node>\n" );
}

sb.append( "</root>" );

return sb.toString();
}

private static void timeIt( String name, Runnable runnable) {
long t0 = System.currentTimeMillis();
runnable.run();
long t1 = System.currentTimeMillis();

System. out .println(name + " executed " + ((t1 - t0) / 1000f) + "sec." );
}
}

* This source code was highlighted with Source Code Highlighter .


そして、これが作業の結果です。

Nodes number=5000
Sum=12497500
Simple iterate executed 17.359sec.
Sum=12497500
Iterate with cloning executed 1.047sec.
Sum=12497500
Iterate with detaching node executed 1.031sec.

Source: https://habr.com/ru/post/J128175/


All Articles