XPathがおもっていたより早かった。

SAXで処理されてるExchangeのCalendarItemを列挙するのをXPathにしようとおもって処理速度計測してみた。絶対SAXのほうが早いと思ってたけど、XPathも結構早かった。そしてDOM早い。

最初、大まかにDOMで取得して、CalendarItemのリスト作ったあと、各種値の取得にXPathを使うでもよいかも。

SAXとかちまちま使ってられんよ。SAXのcharactersで連続呼び出しとか大変だし。

SAX :1401
XPath :2001
DOM :944
package workaround;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class XPathVsSAX {
  static abstract class ElapsedTime {
    public long measure(int loop) throws Exception{
      long start = System.currentTimeMillis();
      for(int i = 0 ; i < loop; i++){
        execute();
      }
      long end = System.currentTimeMillis();
      return end - start;
    }
    abstract void execute() throws Exception;
  }
  /**
   * @param args
   * @throws Exception 
   */
  public static void main(String[] args) throws Exception {
    System.out.println("SAX :" + new ElapsedTime() {
      @Override
      void execute() throws Exception {
        parseBySAXNS();
      }
    }.measure(1000));
    
    System.out.println("XPath :" + new ElapsedTime() {
      @Override
      void execute() throws Exception {
        parseByXPathNS();
      }
    }.measure(1000));
    
    System.out.println("DOM :" + new ElapsedTime() {
      @Override
      void execute() throws Exception {
        parseByDOMNS();
      }
    }.measure(1000));
  }

  private static void parseByDOMNS() throws Exception {
    InputStream xml = ClassLoader.getSystemResourceAsStream("data/event_list.xml");
    DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
    DocumentBuilder b = f.newDocumentBuilder();
    Document d = b.parse(xml);
    NodeList items = d.getElementsByTagNameNS("http://schemas.microsoft.com/exchange/services/2006/types", "CalendarItem");
    xml.close();
  }
  
  private static void parseByXPathNS() throws Exception {
    InputStream xml = ClassLoader.getSystemResourceAsStream("data/event_list.xml");
    XPathFactory f = XPathFactory.newInstance();
  
    XPath path = f.newXPath();
    NameSpaceContextMap nsmap = new NameSpaceContextMap();
    nsmap.put(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI);
    nsmap.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
    nsmap.put("t", "http://schemas.microsoft.com/exchange/services/2006/types");
    nsmap.put("soap", "http://schemas.xmlsoap.org/soap/envelope/");
    nsmap.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
    nsmap.put("xsd", "http://www.w3.org/2001/XMLSchema");
    nsmap.put("m", "http://schemas.microsoft.com/exchange/services/2006/messages");
    
    path.setNamespaceContext(nsmap);
    
    XPathExpression xpath = path.compile("//t:CalendarItem");
    
    NodeList list = (NodeList) xpath.evaluate(new InputSource(xml), XPathConstants.NODESET);
    xml.close();
  }

  private static void parseBySAXNS() throws Exception {
    InputStream xml = ClassLoader.getSystemResourceAsStream("data/event_list_1.xml");
    SAXParserFactory f = SAXParserFactory.newInstance();
    f.setNamespaceAware(true);
    SAXParser p = f.newSAXParser();
    final ArrayList<String> elnames = new ArrayList<String>();
    
    p.parse(xml, new DefaultHandler(){
      static final String EXCHANGE_TYPES = "http://schemas.microsoft.com/exchange/services/2006/types";
      static final String CALENDAR_ITEM = "CalendarItem";
      
      @Override
      public void startElement(String uri, String localName,
          String qName, Attributes attributes) throws SAXException {
        if(uri.equals(EXCHANGE_TYPES) && localName.equals(CALENDAR_ITEM)){
          elnames.add(qName);
        }
        
      }
    });
    xml.close();
    
  }

}
class NameSpaceContextMap extends HashMap<String,String> implements NamespaceContext{

  @Override
  public String getNamespaceURI(String prefix) {
    if(prefix == null){
        throw new NullPointerException("Null prefix");
    }
    String ns = containsKey(prefix) ? get(prefix) : XMLConstants.NULL_NS_URI;
    return ns;
  }

  @Override
  public String getPrefix(String namespaceURI) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Iterator<String> getPrefixes(String namespaceURI) {
    throw new UnsupportedOperationException();
  }
  
}