Java 解析 XML
Java 解析 XML
标签: Java基础
XML解析技术有两种 DOM
SAX
- DOM方式
根据XML的层级结构在内存中分配一个树形结构,把XML的标签,属性和文本等元素都封装成树的节点对象- 优点: 便于实现
增
删
改
查
- 缺点: XML文件过大可能造成内存溢出
- 优点: 便于实现
- SAX方式
采用事件驱动模型边读边解析:从上到下一行行解析,解析到某一元素, 调用相应解析方法- 优点: 不会造成内存溢出,
- 缺点: 查询不方便,但不能实现
增
删
改
不同的公司和组织提供了针对DOM和SAX两种方式的解析器
- SUN的
jaxp
- Dom4j组织的
dom4j
(最常用:如Spring) - JDom组织的
jdom
关于这三种解析器渊源可以参考java解析xml文件四种方式.
JAXP 解析
JAXP是JavaSE的一部分,在javax.xml.parsers
包下,分别针对dom与sax提供了如下解析器:
- Dom
DocumentBuilder
DocumentBuilderFactory
- SAX
SAXParser
SAXParserFactory
示例XML如下,下面我们会使用JAXP对他进行增
删
改
查
操作
- config.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE beans SYSTEM "constraint.dtd">
<beans><bean id="id1" class="com.fq.domain.Bean"><property name="isUsed" value="true"/></bean><bean id="id2" class="com.fq.domain.ComplexBean"><property name="refBean" ref="id1"/></bean>
</beans>
- constraint.dtd
<!ELEMENT beans (bean*) ><!ELEMENT bean (property*)><!ATTLIST beanid CDATA #REQUIREDclass CDATA #REQUIRED><!ELEMENT property EMPTY><!ATTLIST propertyname CDATA #REQUIREDvalue CDATA #IMPLIEDref CDATA #IMPLIED>
JAXP-Dom
/*** @author jifang* @since 16/1/13下午11:24.*/
public class XmlRead {@Testpublic void client() throws ParserConfigurationException, IOException, SAXException {// 生成一个Dom解析器DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();// 解析XML文件Document document = builder.parse(ClassLoader.getSystemResourceAsStream("config.xml"));// ...}
}
DocumentBuilder
的parse(String/File/InputSource/InputStream param)
方法可以将一个XML文件解析为一个Document
对象,代表整个文档.
Document
(org.w3c.dom
包下)是一个接口,其父接口为Node
, Node
的其他子接口还有Element
Attr
Text
等.
Node
Node 常用方法
|
释义 |
---|---|
Node appendChild(Node newChild)
|
Adds the node newChild to the end of the list of children of this node. |
Node removeChild(Node oldChild)
|
Removes the child node indicated by oldChild from the list of children, and returns it. |
NodeList getChildNodes()
|
A NodeList that contains all children of this node. |
NamedNodeMap getAttributes()
|
A NamedNodeMap containing the attributes of this node (if it is an Element) or null otherwise. |
String getTextContent()
|
This attribute returns the text content of this node and its descendants. |
Document
Document 常用方法
|
释义 |
---|---|
NodeList getElementsByTagName(String tagname)
|
Returns a NodeList of all the Elements in document order with a given tag name and are contained in the document. |
Element createElement(String tagName)
|
Creates an element of the type specified. |
Text createTextNode(String data)
|
Creates a Text node given the specified string. |
Attr createAttribute(String name)
|
Creates an Attr of the given name. |
Dom查询
- 解析
<bean/>
标签上的所有属性
public class XmlRead {private Document document;@Beforepublic void setUp() throws ParserConfigurationException, IOException, SAXException {document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(ClassLoader.getSystemResourceAsStream("config.xml"));}@Testpublic void client() throws ParserConfigurationException, IOException, SAXException {NodeList beans = document.getElementsByTagName("bean");for (int i = 0; i < beans.getLength(); ++i) {NamedNodeMap attributes = beans.item(i).getAttributes();scanNameNodeMap(attributes);}}private void scanNameNodeMap(NamedNodeMap attributes) {for (int i = 0; i < attributes.getLength(); ++i) {Attr attribute = (Attr) attributes.item(i);System.out.printf("%s -> %s%n", attribute.getName(), attribute.getValue());// System.out.println(attribute.getNodeName() + " -> " + attribute.getTextContent());}}
}
- 打印XML文件所有标签名
@Test
public void client() {list(document, 0);
}private void list(Node node, int depth) {if (node.getNodeType() == Node.ELEMENT_NODE) {for (int i = 0; i < depth; ++i)System.out.print("\t");System.out.println("<" + node.getNodeName() + ">");}NodeList childNodes = node.getChildNodes();for (int i = 0; i < childNodes.getLength(); ++i) {list(childNodes.item(i), depth + 1);}
}
Dom添加节点
- 在第一个
<bean/>
标签下添加一个<property/>
标签,最终结果形式:
<bean id="id1" class="com.fq.domain.Bean"><property name="isUsed" value="true"/><property name="name" value="simple-bean">新添加的</property>
</bean>
/*** @author jifang* @since 16/1/17 下午5:56.*/
public class XmlAppend {// 文档回写器private Transformer transformer;// xml文档private Document document;@Beforepublic void setUp() throws ParserConfigurationException, IOException, SAXException {document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(ClassLoader.getSystemResourceAsStream("config.xml"));}@Testpublic void client() {// 得到第一bean标签Node firstBean = document.getElementsByTagName("bean").item(0);/** 创建一个property标签 **/Element property = document.createElement("property");// 为property标签添加属性// property.setAttribute("name", "name");// property.setAttribute("value", "feiqing");Attr name = document.createAttribute("name");name.setValue("name");property.setAttributeNode(name);Attr value = document.createAttribute("value");value.setValue("simple-bean");property.setAttributeNode(value);// 为property标签添加内容//property.setTextContent("新添加的");property.appendChild(document.createTextNode("新添加的"));// 将property标签添加到bean标签下firstBean.appendChild(property);}@Afterpublic void tearDown() throws TransformerException {transformer = TransformerFactory.newInstance().newTransformer();// 写回XMLtransformer.transform(new DOMSource(document),new StreamResult("src/main/resources/config.xml"));}
}
注意: 必须将内存中的DOM写回XML文档才能生效
Dom更新节点
- 将刚刚添加的
<property/>
修改如下
<property name="name" value="new-simple-bean">simple-bean是新添加的</property>
@Test
public void client() {NodeList properties = document.getElementsByTagName("property");for (int i = 0; i < properties.getLength(); ++i) {Element property = (Element) properties.item(i);if (property.getAttribute("value").equals("simple-bean")) {property.setAttribute("value", "new-simple-bean");property.setTextContent("simple-bean是新添加的");break;}}
}
Dom删除节点
删除刚刚修改的<property/>
标签
@Test
public void client() {NodeList properties = document.getElementsByTagName("property");for (int i = 0; i < properties.getLength(); ++i) {Element property = (Element) properties.item(i);if (property.getAttribute("value").equals("new-simple-bean")) {property.getParentNode().removeChild(property);break;}}
}
JAXP-SAX
SAXParser
实例需要从SAXParserFactory
实例的newSAXParser()
方法获得, 用于解析XML文件的parse(String uri, DefaultHandler dh)
方法没有返回值,但比DOM方法多了一个事件处理器参数DefaultHandler
:
- 解析到开始标签,自动调用
DefaultHandler
的startElement()
方法; - 解析到标签内容(文本),自动调用
DefaultHandler
的characters()
方法; - 解析到结束标签,自动调用
DefaultHandler
的endElement()
方法.
Sax查询
- 打印整个XML文档
/*** @author jifang* @since 16/1/17 下午9:16.*/
public class SaxRead {@Testpublic void client() throws ParserConfigurationException, IOException, SAXException {SAXParser parser = SAXParserFactory.newInstance().newSAXParser();parser.parse(ClassLoader.getSystemResourceAsStream("config.xml"), new SaxHandler());}private class SaxHandler extends DefaultHandler {@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {System.out.print("<" + qName);for (int i = 0; i < attributes.getLength(); ++i) {String attrName = attributes.getQName(i);String attrValue = attributes.getValue(i);System.out.print(" " + attrName + "=" + attrValue);}System.out.print(">");}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {System.out.print(new String(ch, start, length));}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {System.out.print("</" + qName + ">");}}
}
- 打印所有
property
标签内容的Handler
private class SaxHandler extends DefaultHandler {// 用互斥锁保护isProperty变量private boolean isProperty = false;private Lock mutex = new ReentrantLock();@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {if (qName.equals("property")) {mutex.lock();isProperty = true;}}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {// 只有被锁定之后才有可能是trueif (isProperty) {System.out.println(new String(ch, start, length));}}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {if (qName.equals("property")) {try {isProperty = false;} finally {mutex.unlock();}}}
}
注: SAX方式不能实现
增
删
改
操作.
Dom4j解析
Dom4j是JDom的一种智能分支,从原先的JDom组织中分离出来,提供了比JDom功能更加强大,性能更加卓越的Dom4j解析器(比如提供对XPath支持).
使用Dom4j需要在pom中添加如下依赖:
<dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>
</dependency>
示例XML如下,下面我们会使用Dom4j对他进行增
删
改
查
操作:
- config.xml
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.fq.me/context"xsi:schemaLocation="http://www.fq.me/context http://www.fq.me/context/context.xsd"><bean id="id1" class="com.fq.benz"><property name="name" value="benz"/></bean><bean id="id2" class="com.fq.domain.Bean"><property name="isUsed" value="true"/><property name="complexBean" ref="id1"/></bean>
</beans>
- context.xsd
<?xml version="1.0" encoding="utf-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"targetNamespace="http://www.fq.me/context"elementFormDefault="qualified"><element name="beans"><complexType><sequence><element name="bean" maxOccurs="unbounded"><complexType><sequence><element name="property" maxOccurs="unbounded"><complexType><attribute name="name" type="string" use="required"/><attribute name="value" type="string" use="optional"/><attribute name="ref" type="string" use="optional"/></complexType></element></sequence><attribute name="id" type="string" use="required"/><attribute name="class" type="string" use="required"/></complexType></element></sequence></complexType></element>
</schema>
/*** @author jifang* @since 16/1/18下午4:02.*/
public class Dom4jRead {@Testpublic void client() throws DocumentException {SAXReader reader = new SAXReader();Document document = reader.read(ClassLoader.getSystemResource("config.xml"));// ...}
}
与JAXP类似Document
也是一个接口(org.dom4j
包下),其父接口是Node
, Node
的子接口还有Element
Attribute
Document
Text
CDATA
Branch
等
Node
Node 常用方法
|
释义 |
---|---|
Element getParent()
|
getParent returns the parent Element if this node supports the parent relationship or null if it is the root element or does not support the parent relationship. |
Document
Document 常用方法
|
释义 |
---|---|
Element getRootElement()
|
Returns the root Elementfor this document. |
Element
Element 常用方法
|
释义 |
---|---|
void add(Attribute/Text param)
|
Adds the given Attribute/Text to this element. |
Element addAttribute(String name, String value)
|
Adds the attribute value of the given local name. |
Attribute attribute(int index)
|
Returns the attribute at the specified indexGets the |
Attribute attribute(String name)
|
Returns the attribute with the given name |
Element element(String name)
|
Returns the first element for the given local name and any namespace. |
Iterator elementIterator()
|
Returns an iterator over all this elements child elements. |
Iterator elementIterator(String name)
|
Returns an iterator over the elements contained in this element which match the given local name and any namespace. |
List elements()
|
Returns the elements contained in this element. |
List elements(String name)
|
Returns the elements contained in this element with the given local name and any namespace. |
Branch
Branch 常用方法
|
释义 |
---|---|
Element addElement(String name)
|
Adds a new Element node with the given name to this branch and returns a reference to the new node. |
boolean remove(Node node)
|
Removes the given Node if the node is an immediate child of this branch. |
Dom4j查询
- 打印所有属性信息:
/*** @author jifang* @since 16/1/18下午4:02.*/
public class Dom4jRead {private Document document;@Beforepublic void setUp() throws DocumentException {document = new SAXReader().read(ClassLoader.getSystemResource("config.xml"));}@Test@SuppressWarnings("unchecked")public void client() {Element beans = document.getRootElement();for (Iterator iterator = beans.elementIterator(); iterator.hasNext(); ) {Element bean = (Element) iterator.next();String id = bean.attributeValue("id");String clazz = bean.attributeValue("class");System.out.println("id: " + id + ", class: " + clazz);scanProperties(bean.elements());}}public void scanProperties(List<? extends Element> properties) {for (Element property : properties) {System.out.print("name: " + property.attributeValue("name"));Attribute value = property.attribute("value");if (value != null) {System.out.println("," + value.getName() + ": " + value.getValue());}Attribute ref = property.attribute("ref");if (ref != null) {System.out.println("," + ref.getName() + ": " + ref.getValue());}}}
}
Dom4j添加节点
在第一个<bean/>
标签末尾添加<property/>
标签
<bean id="id1" class="com.fq.benz"> <property name="name" value="benz"/> <property name="refBean" ref="id2">新添加的标签</property>
</bean>
/*** @author jifang* @since 16/1/19上午9:50.*/
public class Dom4jAppend {//...@Testpublic void client() {Element beans = document.getRootElement();Element firstBean = beans.element("bean");Element property = firstBean.addElement("property");property.addAttribute("name", "refBean");property.addAttribute("ref", "id2");property.setText("新添加的标签");}@Afterpublic void tearDown() throws IOException {// 回写XMLOutputFormat format = OutputFormat.createPrettyPrint();XMLWriter writer = new XMLWriter(new FileOutputStream("src/main/resources/config.xml"), format);writer.write(document);}
}
我们可以将获取读写XML操作封装成一个工具, 以后调用时会方便些:
/*** @author jifang* @since 16/1/19下午2:12.*/
public class XmlUtils {public static Document getXmlDocument(String config) {try {return new SAXReader().read(ClassLoader.getSystemResource(config));} catch (DocumentException e) {throw new RuntimeException(e);}}public static void writeXmlDocument(String path, Document document) {try {new XMLWriter(new FileOutputStream(path), OutputFormat.createPrettyPrint()).write(document);} catch (IOException e) {throw new RuntimeException(e);}}
}
- 在第一个
<bean/>
的第一个<property/>
后面添加一个<property/>
标签
<bean id="id1" class="com.fq.benz"> <property name="name" value="benz"/> <property name="rate" value="3.14"/><property name="refBean" ref="id2">新添加的标签</property>
</bean>
public class Dom4jAppend {private Document document;@Beforepublic void setUp() {document = XmlUtils.getXmlDocument("config.xml");}@Test@SuppressWarnings("unchecked")public void client() {Element beans = document.getRootElement();Element firstBean = beans.element("bean");List<Element> properties = firstBean.elements();//Element property = DocumentHelper// .createElement(QName.get("property", firstBean.getNamespaceURI()));Element property = DocumentFactory.getInstance().createElement("property", firstBean.getNamespaceURI());property.addAttribute("name", "rate");property.addAttribute("value", "3.14");properties.add(1, property);}@Afterpublic void tearDown() {XmlUtils.writeXmlDocument("src/main/resources/config.xml", document);}
}
Dom4j修改节点
- 将
id1
bean
的第一个<property/>
修改如下:
<property name="name" value="翡青"/>
@Test
@SuppressWarnings("unchecked")
public void client() {Element beans = document.getRootElement();Element firstBean = beans.element("bean");List<Element> properties = firstBean.elements();Element property = DocumentFactory.getInstance().createElement("property", firstBean.getNamespaceURI());property.addAttribute("name", "rate");property.addAttribute("value", "3.14");properties.add(1, property);
}
Dom4j 删除节点
- 删除刚刚修改的节点
@Test
@SuppressWarnings("unchecked")
public void delete() {List<Element> beans = document.getRootElement().elements("bean");for (Element bean : beans) {if (bean.attributeValue("id").equals("id1")) {List<Element> properties = bean.elements("property");for (Element property : properties) {if (property.attributeValue("name").equals("name")) {// 执行删除动作property.getParent().remove(property);break;}}break;}}
}
Dom4j实例
在Java 反射一文中我们实现了根据JSON
配置文件来加载bean
的对象池,现在我们可以为其添加根据XML配置(XML文件同前):
/*** @author jifang* @since 16/1/18下午9:18.*/
public class XmlParse {private static final ObjectPool POOL = ObjectPoolBuilder.init(null);public static Element parseBeans(String config) {try {return new SAXReader().read(ClassLoader.getSystemResource(config)).getRootElement();} catch (DocumentException e) {throw new RuntimeException(e);}}public static void processObject(Element bean, List<? extends Element> properties)throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {Class<?> clazz = Class.forName(bean.attributeValue(CommonConstant.CLASS));Object targetObject = clazz.newInstance();for (Element property : properties) {String fieldName = property.attributeValue(CommonConstant.NAME);Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);// 含有value属性if (property.attributeValue(CommonConstant.VALUE) != null) {SimpleValueSetUtils.setSimpleValue(field, targetObject, property.attributeValue(CommonConstant.VALUE));} else if (property.attributeValue(CommonConstant.REF) != null) {String refId = property.attributeValue(CommonConstant.REF);Object object = POOL.getObject(refId);field.set(targetObject, object);} else {throw new RuntimeException("neither value nor ref");}}POOL.putObject(bean.attributeValue(CommonConstant.ID), targetObject);}
}
注: 上面代码只是对象池项目的XML解析部分,完整项目可参考git@git.oschina.net:feiqing/commons-frame.git
XPath
XPath是一门在XML文档中查找信息的语言,XPath可用来在XML文档中对元素和属性进行遍历.
表达式 | 描述 |
---|---|
/
|
从根节点开始获取(/beans :匹配根下的<beans/> ; /beans/bean :匹配<beans/> 下面的<bean/> )
|
//
|
从当前文档中搜索,而不用考虑它们的位置(//property : 匹配当前文档中所有<property/> )
|
*
|
匹配任何元素节点(/* : 匹配所有标签)
|
@
|
匹配属性(例: //@name : 匹配所有name 属性)
|
[position]
|
位置谓语匹配(例: //property[1] : 匹配第一个<property/> ;//property[last()] : 匹配最后一个<property/> )
|
[@attr]
|
属性谓语匹配(例: //bean[@id] : 匹配所有带id属性的标签; //bean[@id='id1'] : 匹配所有id属性值为’id1’的标签)
|
谓语: 谓语用来查找某个特定的节点或者包含某个指定的值的节点.
XPath的语法详细内容可以参考W3School XPath 教程.
Dom4j对XPath的支持
默认的情况下Dom4j并不支持XPath, 需要在pom下添加如下依赖:
<dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version>
</dependency>
Dom4jNode
接口提供了方法对XPath支持:
方法 |
---|
List selectNodes(String xpathExpression)
|
List selectNodes(String xpathExpression, String comparisonXPathExpression)
|
List selectNodes(String xpathExpression, String comparisonXPathExpression, boolean removeDuplicates)
|
Object selectObject(String xpathExpression)
|
Node selectSingleNode(String xpathExpression)
|
XPath实现查询
- 查询所有
bean
标签上的属性值
/*** @author jifang* @since 16/1/20上午9:28.*/
public class XPathRead {private Document document;@Beforepublic void setUp() throws DocumentException {document = XmlUtils.getXmlDocument("config.xml");}@Test@SuppressWarnings("unchecked")public void client() {List<Element> beans = document.selectNodes("//bean");for (Element bean : beans) {System.out.println("id: " + bean.attributeValue("id") +", class: " + bean.attributeValue("class"));}}
}
XPath实现更新
- 删除id=”id2”的
<bean/>
@Test
public void client() {Node bean = document.selectSingleNode("//bean[@id=\"id2\"]");bean.getParent().remove(bean);
}
- 参考:
- Dom4j的使用
- Java 处理 XML 的三种主流技术及介绍
Java 解析 XML相关推荐
- 详解Java解析XML的四种方法
http://developer.51cto.com 2009-03-31 13:12 cnlw1985 javaeye 我要评论(8) XML现在已经成为一种通用的数据交换格式,平台的无关性 ...
- java解析xml的三种方法
java解析XML的三种方法 1.SAX事件解析 package com.wzh.sax;import org.xml.sax.Attributes; import org.xml.sax.SAXEx ...
- xml教程之java解析xml文档
1.java解析xml文件介绍 XML解析方式分为两种:DOM方式和SAX方式 DOM:Document Object Model,文档对象模型.这种方式是W3C推荐的处理XML的一种方式. SAX: ...
- Java解析XML汇总(DOM/SAX/JDOM/DOM4j/XPath)
http://blog.csdn.net/smcwwh/article/details/7183869 关键字:Java解析xml.解析xml四种方法.DOM.SAX.JDOM.DOM4j.XPath ...
- java解析xml的几种方式
java解析xml的几种方式 博客分类: java基础备忘-好记性不然烂笔头 XMLJava应用服务器数据结构编程 第一种:DOM. DOM的全称是Document Object Model,也即文 ...
- java解析xml文件
使用java解析xml文件,通过dom4j. 代码如下: package com.java.team; import java.io.File; import java.util.ArrayList; ...
- 在java解析XML写入到数据库中遇到的问题
用java程序解析XML写入到MYSQL中遇到了很多问题. 1,关于XML JAVA解析XML的时候,一定要处理没有读取到XML.读取到XML但是里面的元素节点不存在的问题,不然会出很多问题. 2.数 ...
- XML解析 (JAVA解析xml文件)java+Dom4j+Xpath xml文件解析根据子节点得到父节点 查找校验xml文件中相同的节点属性值 java遍历文件夹解析XML
XML解析 (JAVA解析xml文件)java+Dom4j+Xpath xml文件解析根据子节点得到父节点 以及查找xml文件中相同的节点属性值 项目背景:这是本人实习中所碰到的项目,当时感觉很棘手, ...
- java解析XML saxReader.read(xml) 错误:org.dom4j.DocumentException: no protocol
java解析XML saxReader.read(xml) 错误:org.dom4j.DocumentException: no protocol 完整错误信息: org.dom4j.Document ...
最新文章
- ORB_SLAM2单目初始化策略
- Getting Started with OpenCV
- Filebeat的下载(图文讲解)
- 广西2021各校高考成绩查询入口,2021年广西高考成绩排名查询系统,广西高考位次排名查询...
- arcgis 字段计算器 条件赋值_【教程】ArcGIS入门教程(11)——多条件购房分析...
- (pytorch-深度学习)SE-ResNet的pytorch实现
- leetcode —— 41. 缺失的第一个正数
- NHibernate文档翻译--体系结构
- Objective-c 中 nil, Nil, NULL和NSNull的区别
- BZOJ-1050-[HAOI2006]旅行comf(并查集)
- WinRAR去广告方法
- 这就是为什么上帝和魔鬼都喜欢数学......
- 笔记本拔插电源黑屏问题
- [Matlab] 单次测量的中误差、算数平均值的中误差、加权求最或然值、函数拟合、莫迪图的计算实例
- 利用VBA向Word中批量添加图片以及整理成Excel数据的图片名
- 平面点集的凸包问题的算法分析
- 沪漂程序媛妹子的一天...
- 组织项目管理(PMP知识整理)
- ora-01172 ora-01151的处理
- 复杂因子秒级计算,文谛资产是这样做到的...
热门文章
- epoll 系列系统调用
- pthread_cond_t
- 谈谈Intranet与Internet的区别和联系
- 脏牛Linux本地提权漏洞复现(CVE-2016-5195、Linux、Android、提权)
- docker ubuntu16.04镜像下安装cowrie蜜罐记录
- Linux系统编程:验证kernel内核缓存区大小-4096字节
- Java实现 栈 和 队列
- Python学习教程(Python学习路线):如何正确使用Pandas库提升项目的运行速度?...
- [cogs1065]绿豆蛙的归宿
- Hibernate的核心API