Esper入门简介:三、 对Esper底层数据结构特点分析,数据的入、出
来源:互联网 发布:知识分子的鸦片 知乎 编辑:程序博客网 时间:2024/06/05 21:51
package com.doctor.esper.reference;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.doctor.esper.common.EsperUtil;import com.doctor.esper.tutorial.OrderEvent;import com.espertech.esper.client.Configuration;import com.espertech.esper.client.EPServiceProvider;import com.espertech.esper.client.EPServiceProviderManager;import com.espertech.esper.client.EPStatement;import com.espertech.esper.client.EventBean;import com.espertech.esper.client.soda.StreamSelector;/** * Chapter 3. Processing Model Filters and Where-clauses * * * 1.esper处理模型是持续的,像jstorm流一样。esper引擎接收到事件流,相应Statement的更新监听器和订阅者会收到更新的数据 * (它门的处理和Statement所选择的事件流、视图、过滤器和输出速率有关系). * * @author doctor * * @time 2015年6月1日 下午2:11:12 */public class Chapter3ProcessingModelFiltersAndWhereClauses {private static final Logger log = LoggerFactory.getLogger(Chapter3ProcessingModelFiltersAndWhereClauses.class);public static void main(String[] args) {Configuration configuration = new Configuration();configuration.addEventTypeAutoName("com.doctor.esper.tutorial"); // 设置事件更新输出内容。configuration.getEngineDefaults().getStreamSelection().setDefaultStreamSelector(StreamSelector.RSTREAM_ISTREAM_BOTH);EPServiceProvider epServiceProvider = EPServiceProviderManager.getDefaultProvider(configuration);// 3.2. Insert Stream// 下面的EPStatement选择存储所有OrderEvent。每当esper引擎处理OrderEvent事件流或者OrderEvent子类型的事件流,// epser引擎会触发该EPStatement的监听器。log.info("{msg:'3.2. Insert Stream'}");String expression = "select * from OrderEvent";EPStatement epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);OrderEvent orderEvent = new OrderEvent("shirt", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("aaa", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("bbb", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement));// 输出结果:每当事件流流向EPStatement存储结构时候,都会触发更新监听器。newEvents就代表新进来的数据// 输出的list为空,知道为什么吗?因为没定义存储结构,像数据窗口这样的结构。// 06-01 14:43:01.648 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"shirt","price":75.5}}// 06-01 14:43:01.744 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"aaa","price":35.5}}// 06-01 14:43:01.745 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"bbb","price":85.5}}// {list:[]}// 3.3. Insert and Remove Stream 容量限制窗口,像FIFO缓存,只保存事件流中最后N个事件log.info("{msg:'3.3. Insert and Remove Stream'}");epServiceProvider.getEPAdministrator().destroyAllStatements();epServiceProvider.removeAllStatementStateListeners();expression = "select * from OrderEvent.win:length(1)";epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);orderEvent = new OrderEvent("1", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("2", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("3", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("4", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement));// 输出结果:EPStatement限制存储容量为2.事件体现了进、出容量的变化。newEvents表示新事件出现,oldEvents表示EPStatement// 容量限制丢弃的老事件。// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"2","price":35.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {oldEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {oldEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"2","price":35.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"4","price":85.5}}// 06-01 15:22:20.552 main INFO c.d.e.r.Chapter3ProcessingModel - {oldEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}}// {list:[BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent// bean={"itemName":"4","price":85.5}]}// 3.4. Filters and Where-clauses 事件流过滤器允许什么样的事件流向数据窗口。// Filters=》只有符合事件流过滤器的事件才能真正的进入数据窗口并且触发相应的监听器// Where-clauses 和have 语句 ==》事件流进入数据窗口或视图后的结果查询,并不影响事件流进入数据窗口。log.info("{msg:'3.4. Filters and Where-clauses'}");epServiceProvider.getEPAdministrator().destroyAllStatements();epServiceProvider.removeAllStatementStateListeners();expression = "select * from OrderEvent(price > 50 ).win:length(3)";epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);orderEvent = new OrderEvent("1", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("2", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("3", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("4", 5.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement));// 06-01 15:32:24.301 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"4","price":185.5}}log.info("{msg:'3.4. Where-clauses'}");epServiceProvider.getEPAdministrator().destroyAllStatements();epServiceProvider.removeAllStatementStateListeners();expression = "select * from OrderEvent.win:length(3) where price > 50 ";epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);orderEvent = new OrderEvent("1", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("2", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("3", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("4", 5.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement));}public static void update(EventBean[] newEvents, EventBean[] oldEvents) {if (newEvents != null && newEvents[0] != null) {log.info("{newEvents:{}}", newEvents[0]);}if (oldEvents != null && oldEvents[0] != null) {log.info("{oldEvents:{}}", oldEvents[0]);}}}
解析:
1、
configuration.addEventTypeAutoName("com.doctor.esper.tutorial");
上面的配置是为了EPL表达式中事件名称的简写。
如:select * from OrderEvent,OrderEvent就不用写全名路径:com.doctor.esper.tutorial.OrderEvent,是不是想起来Mybatis中xml配置中的sql也有这样的配置吧。
2、
configuration.getEngineDefaults().getStreamSelection().setDefaultStreamSelector(StreamSelector.RSTREAM_ISTREAM_BOTH);
这个配置,主要是为了观察数据从Esper内存中删除,也可以触发监听器、订阅者。默认配置只有数据进入内存结构时候才会触发。
3、
// 3.2. Insert Stream// 下面的EPStatement选择存储所有OrderEvent。每当esper引擎处理OrderEvent事件流或者OrderEvent子类型的事件流,// epser引擎会触发该EPStatement的监听器。log.info("{msg:'3.2. Insert Stream'}");String expression = "select * from OrderEvent";EPStatement epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);OrderEvent orderEvent = new OrderEvent("shirt", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("aaa", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("bbb", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement));// 输出结果:每当事件流流向EPStatement存储结构时候,都会触发更新监听器。newEvents就代表新进来的数据// 输出的list为空,知道为什么吗?因为没定义存储结构,像数据窗口这样的结构。// 06-01 14:43:01.648 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"shirt","price":75.5}}// 06-01 14:43:01.744 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"aaa","price":35.5}}// 06-01 14:43:01.745 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"bbb","price":85.5}}// {list:[]}
从EPL select * from OrderEvent,得知,该事件(内存数据结构)并不保持事件,事件流进入时候,会触发监听器,但数据从来没有保存。没进入这个内存结构,也就无所谓数据删除,就没有事件删除。从上面的输出内容,可以看出,发送了三个订单事件,监听器也输出了新事件的到达。
4、
// 3.3. Insert and Remove Stream 容量限制窗口,像FIFO缓存,只保存事件流中最后N个事件log.info("{msg:'3.3. Insert and Remove Stream'}");epServiceProvider.getEPAdministrator().destroyAllStatements();epServiceProvider.removeAllStatementStateListeners();expression = "select * from OrderEvent.win:length(1)";epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);orderEvent = new OrderEvent("1", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("2", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("3", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("4", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement));// 输出结果:EPStatement限制存储容量为2.事件体现了进、出容量的变化。newEvents表示新事件出现,oldEvents表示EPStatement// 容量限制丢弃的老事件。// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"2","price":35.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {oldEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {oldEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"2","price":35.5}}// 06-01 15:22:20.551 main INFO c.d.e.r.Chapter3ProcessingModel - {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"4","price":85.5}}// 06-01 15:22:20.552 main INFO c.d.e.r.Chapter3ProcessingModel - {oldEvents:BeanEventBean eventType=BeanEventType name=OrderEvent// clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}}// {list:[BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent// bean={"itemName":"4","price":85.5}]}
现在,我们用select * from OrderEvent.win:length(1),定义只保存最新的那个事件(窗口视图长度为1)。从上面的输出结果可以看出像FIFO缓存一样,新事件到达,老事件就会丢弃。
5、
// 3.4. Filters and Where-clauses 事件流过滤器允许什么样的事件流向数据窗口。// Filters=》只有符合事件流过滤器的事件才能真正的进入数据窗口并且触发相应的监听器// Where-clauses 和have 语句 ==》事件流进入数据窗口或视图后的结果查询,并不影响事件流进入数据窗口。log.info("{msg:'3.4. Filters and Where-clauses'}");epServiceProvider.getEPAdministrator().destroyAllStatements();epServiceProvider.removeAllStatementStateListeners();expression = "select * from OrderEvent(price > 50 ).win:length(3)";epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);orderEvent = new OrderEvent("1", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("2", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("3", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("4", 5.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement)); // {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}} // {newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}} // {list:[BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}, BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}]}
我们可以用窗口Filters定义什么样的事件可以流进定义的事件流(内存结构)。
如:OrderEvent(price > 50 ).win:length(3)。订单只有大于50个单位的才能进入这个事件流。只有1,3进入了,而且最后我们用EsperEsper给的查询查出了只有2个事件。事件进入了定义的事件流(内存结构),当然会触发监听器,新事件到达。
EsperUtil工具如下:
package com.doctor.esper.common;import java.util.ArrayList;import java.util.List;import java.util.stream.Collectors;import java.util.stream.Stream;import com.espertech.esper.client.Configuration;import com.espertech.esper.client.EPOnDemandPreparedQuery;import com.espertech.esper.client.EPOnDemandPreparedQueryParameterized;import com.espertech.esper.client.EPOnDemandQueryResult;import com.espertech.esper.client.EPServiceProvider;import com.espertech.esper.client.EPServiceProviderManager;import com.espertech.esper.client.EPStatement;import com.espertech.esper.client.EventBean;import com.espertech.esper.client.SafeIterator;/** * @author docotr * * @time 2015年6月1日 下午4:28:33 */public enum EsperUtil {;public static List<EventBean> get(EPStatement epStatement) {List<EventBean> list = new ArrayList<>();SafeIterator<EventBean> safeIterator = epStatement.safeIterator();try {while (safeIterator.hasNext()) {list.add(safeIterator.next());}} catch (Throwable e) {safeIterator.close();e.printStackTrace();} finally {safeIterator.close();}return list;}/** * @see 15.2. The Service Provider Interface * * EPServiceProvider就代表一个esper引擎实例。 一个esper引擎的实例与其它引擎的实例是没有关联性的(独立性)。 * 每个引擎的实例都有自己的administrative and runtime interface. * 一个引擎的实例有许多个,获得实例的EPServiceProviderManager方法为getDefaultProvider和getProvider(String providerURI)。 * 后者可以用于根据不同的获得providerURI,引擎可以生成不同实例。EPServiceProviderManager根据providerURI存在与否, * 获取已经存在的实例或者新的实例。 * * * @param config * @return */public static EPServiceProvider esperConfig(String config) {Configuration configuration = new Configuration();configuration.configure(EsperUtil.class.getClassLoader().getResource(config));return EPServiceProviderManager.getDefaultProvider(configuration);}public static List<EventBean> executeQuery(EPServiceProvider epServiceProvider, String epl) {EPOnDemandQueryResult result = epServiceProvider.getEPRuntime().executeQuery(epl);return Stream.of(result.getArray()).collect(Collectors.toList());}public static List<EventBean> prepareQuery(EPServiceProvider epServiceProvider, String epl) {EPOnDemandPreparedQuery preparedQuery = epServiceProvider.getEPRuntime().prepareQuery(epl);EPOnDemandQueryResult result = preparedQuery.execute();return Stream.of(result.getArray()).collect(Collectors.toList());}public static List<EventBean> prepareQueryWithParameters(EPServiceProvider epServiceProvider, String epl, Object... parameter) {EPOnDemandPreparedQueryParameterized queryParameterized = epServiceProvider.getEPRuntime().prepareQueryWithParameters(epl);if (parameter != null) {for (int i = 0, length = parameter.length; i < length; i++) {queryParameterized.setObject(i + 1, parameter[i]);}}EPOnDemandQueryResult result = epServiceProvider.getEPRuntime().executeQuery(queryParameterized);return Stream.of(result.getArray()).collect(Collectors.toList());}}
6、
log.info("{msg:'3.4. Where-clauses'}");epServiceProvider.getEPAdministrator().destroyAllStatements();epServiceProvider.removeAllStatementStateListeners();expression = "select * from OrderEvent.win:length(3) where price > 50 ";epStatement = epServiceProvider.getEPAdministrator().createEPL(expression);epStatement.addListener(Chapter3ProcessingModelFiltersAndWhereClauses::update);orderEvent = new OrderEvent("1", 75.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("2", 35.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("3", 85.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);orderEvent = new OrderEvent("4", 5.50D);epServiceProvider.getEPRuntime().sendEvent(orderEvent);log.info("{list:{}}", EsperUtil.get(epStatement));
Where-clauses看看对监听器的影响。输出结果:
{newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}}
{newEvents:BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}}
{oldEvents:BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"1","price":75.5}}
{list:[BeanEventBean eventType=BeanEventType name=OrderEvent clazz=com.doctor.esper.tutorial.OrderEvent bean={"itemName":"3","price":85.5}]}
对于上面的输出,由文档和结果对比可知:Where-clauses不影响事件的流入,当newEvents 1,2,3进入之后,达到窗口大小3限制,所以oldEvents就是1了,所以触发老事件丢弃监听器报告老事件删去了。可newEvents事件触发只有1,3,因为它们的订单价格大于50,才会触发新事件到达。
所以区分开了吗。最后的查询也只有符合where条件的才能查出(sql也是这样的)
/**
* 3.4. Filters and Where-clauses
* 时间/长度窗口的Filters影响了事件能不能进入这个数据结构。
* 而且间接影响了监听器(数据没进入,就不会触发new事件)。
* where 条件与监听器有关系,符合where条件的才会触发监听器。
* 而且还影响select 结果。
*
* 即:Filters and Where-clauses都影响监听器和select结果。
* 不同的是能否进入数据窗口
- Esper入门简介:三、 对Esper底层数据结构特点分析,数据的入、出
- Esper入门简介:二 个人对它的理解
- Esper入门简介:一
- 对esper的理解
- esper简介
- Esper入门简介:四、 由上篇三我们实现个简单的报警作用吧
- Esper企业版介绍(三)Esper推送服务--从CEP层到WEB层的数据推送
- Esper
- Esper
- esper
- esper 对doc的翻译和注解
- Esper-技术简介
- Esper-技术简介
- Esper-技术简介
- Esper-技术简介
- Esper学习和原理分析
- Esper的POJO事件处理
- Esper事件处理引擎_1_JavaBean 数据结构处理
- AOJ-747-镜像树
- [转] js遍历 子节点 子元素
- javascript的内置对象
- 在VS2010上使用C#调用非托管C++生成的DLL文件
- Windows注册表
- Esper入门简介:三、 对Esper底层数据结构特点分析,数据的入、出
- R,L,C,t物理量之间的量纲关系
- Java 实现断点续传 (HTTP)
- QT:十六进制字符串转数字整形
- 循环语句
- shape和selector
- 最近的想法
- mysql 字符集更改与导入数据
- 动漫人物之游戏人生:ステファニー・ドーラ 史蒂芬妮·多拉 CV 日笠陽子