当前位置:天才代写 > tutorial > JAVA 教程 > 实现高机能Java理会器

实现高机能Java理会器

2017-11-02 08:00 星期四 所属: JAVA 教程 浏览:495

副标题#e#

备注: 本篇文章是关于先前沟通主题文章的最新版本。先前文章主要先容建设高机能理会器的一些要点,但它接收了读者的一部门品评发起。本来的文章举办了全面修订,并增补了相对完整的代码。我们但愿你喜欢本次更新。

假如你没有指定命据或语言尺度的或开源的Java理会器, 大概常常要用Java实现你本身的数据或语言理会器。可能,大概有许多理会器可选,可是要么太慢,要么太耗内存,可能没有你需要的特定成果。可能开源理会器存在缺陷,可能开源理会器项目被打消诸如此类原因。上述原因都没有你将需要实现你本身的理会器的事实重要。

当你必须实现本身的理会器时,你会但愿它有精采表示,机动,成果富厚,易于利用,最后但更重要是易于实现,究竟你的名字会呈此刻代码中。本文中,我将先容一种用Java实现高机能理会器的方法。该要领不具排他性,它是简约的,并实现了高机能和公道的模块化设计。该设计灵感来历于VTD-XML ,我所见到的最快的java XML理会器,比StAX和SAX Java尺度XML理会器更快。

两个根基理会器范例

理会器有多种分类方法。在这里,我只较量两个根基理会器范例的区别:

顺序会看法析器(Sequential access parser)

随时机看法析器(Random access parser)

顺序会见意思是理会器理会数据,理会完毕后将理会数据移交给数据处理惩罚器。数据处理惩罚器只会见当前已理会过的数据;它不能转头处理惩罚先前的数据和处理惩罚前面的数据。顺序会看法析器已经很常见,甚至作为基准理会器,SAX和StAX理会器就是最知名的例子。

随时机看法析器是可以在已理会的数据上或让数据处理惩罚代码向前和向后(随时机见)。随时机看法析器例子见XML DOM理会器。

实现高性能Java剖析器

顺序会看法析器只能让你在文档流中会见刚理会过的“窗口”或“事件”,而随时机看法析器答允你凭据想要的方法会见遍历。

设计提要

我这里先容的理会器设计属于随时机见变种。

随时机看法析器实现老是比顺序会看法析器慢一些,这是因为它们一般成立在某种已理会数据工具树上,数据处理惩罚器能会见上述数据。建设工具树实际上在CPU时钟上是慢的,而且淹灭大量内存。

取代在理会数据上构建工具树,更高机能的方法是成立指向原始数据缓存的索引缓存。索引指向已理会数据的元素起始点和终点。取代通过工具树会见数据,数据处理惩罚代码直接在含有原始数据的缓存中会见已理会数据。如下是两种要领的示意图:

实现高性能Java剖析器

因为没找到更好的名字,我就叫该理会器为“索引叠加理会器”。该理会器在原始数据上新建了一个索引叠加层。这个让人想起数据库构建存储在硬盘上的数据索引的方法。它在原始未处理惩罚的数据上建设了指针,让欣赏和搜索数据更快。

如前所说,该设计受VTD-XML的开导, VTD是虚拟令牌描写符(Virtual Token Descriptor)的英文缩写。因此,你可以叫它虚拟令牌描写符理会器。不外,我更喜欢索引叠加的定名,因为这是虚拟令牌描写符代表,在原始数据上的索引。


#p#副标题#e#

通例理会器设计

一般理会器设计会将理会进程分为两步。第一步将数据解析为内聚的令牌,令牌是一个或多个已理会数据的字节或字符。第二步表明这些令牌并基于这些令牌构建更大的元素。两步示意图如下:

实现高性能Java剖析器

图中元素并不是指XML元素(尽量XML元素也理会元素),而更大“数据元素”结构了已理会数据。在我XML文档中暗示XML元素,而在JSON 文档中则暗示JSON工具,诸如此类。

举例说明,字符串将被解析为如命令牌:

<
myelement
>

一旦数据解析为多个令牌,理会器更容易领略它们和判定这些令牌结构的大元素。理会器将会识别XML元素以 ‘<’令牌开头后头是字符串令牌(元素名称),然后是一系列可选的属性,最后是‘>’令牌。

索引叠加理会器设计

两步要领也将用于我们的理会器设计。输入数据首先由阐明器组件解析为多个令牌。 然后理会器理会这些令牌识别输入数据的大元素界线。

你也可以增加可选的第三步调—“元素导航步调”到理会进程中。 若理会器从已理会数据中结构工具树,那么工具树一般会包括工具树导航的链接。当我们构建元素索引缓存取代工具树时,我们需要一个独立组件辅佐数据处理惩罚代码导航元素索引缓存。

我们理会器设计概览拜见如下示意图:

实现高性能Java剖析器

我们首先将所有数据读到数据缓存内。为了担保可以通过理会中建设的索引随时机见原始数据,所有原始数据必须放到内存中。

#p#分页标题#e#

接着,阐明器将数据解析为多个令牌。开始索引,竣事索引和令牌范例城市生存于阐明器中一个内部令牌缓存。利用令牌缓存使其向前和向后会见成为大概,上述环境下理会器需要令牌缓存。

第三步,理会器查找从阐明器获取的令牌,在上下文中校验它们,并判定它们暗示的元素。然后,理会器基于阐明器获取的令牌结构元素索引(索引叠加)。理会器逐一得到来自阐明器的令牌。因此,阐明器实际上不需要马大将所有数据解析成令牌。而仅仅是在特按时间点找到一个令牌。

数据处理惩罚代码能会见元素缓存,并用它会见原始数据。可能,你大概会将数据缓存封装到元素会见组件中,让会见元素缓存更容易。

该设计基于已理会数据构建工具树,但它需成立会见布局—元素缓存,由索引(整型数组)指向含有原始数据的数据缓存。我们能利用这些索引会见存于原始数据缓存的数据。

下面小节将从设计的差异方面更具体地举办先容。

数据缓存

数据缓存是含有原始数据的一种字节或字符缓存。令牌缓存和元素缓存持有数据缓存的索引。

为了随时机看法析过了的数据,内存暗示上述信息的机制是须要的。我们不利用工具树而是用包括原始数据的数据缓存。

将所有数据放在内存中需耗损大块的内存。若数据含有的元素是彼此独立的,如日志记录,将整个日志文件放在内存中将是过犹不及了。相反,你可以拉大块的日志文件,该文件存有完整的日志记录。因为每个日志记录可完全理会,而且独立于其它日志记录的处理惩罚,所以我们不需要在同一时间将整个日志文件放到内存中。在我的文章—“利用缓存迭代会见数据流”中,我已经描写了如何遍历块中的数据流。

标志阐明器和标志缓存

阐明器将数据缓解析为多个令牌。令牌信息存储在令牌缓存中,包括如下内容:

令牌定位(起始索引)

令牌长度

令牌范例 (可选)

上述信息放在数组中。如下实例说明处理惩罚逻辑:

public class IndexBuffer {
    public int[] position = null;
    public int[] length = null;
    public byte[] type = null; 
/* assuming a max of 256 types (1 byte / type) */
}

当阐明器找到数据缓存中令牌时,它将构建位置数组的起始索引位置,长度数组的令牌长度和范例数组的令牌范例。

若不利用可选的令牌范例数组,你仍能通过查察令牌数据来区分令牌范例。这是机能和内存耗损的衡量。

#p#副标题#e#

理会器

理会器是在性质上与阐明器雷同,只不外它回收令牌作为输入和输出的元素索引。如同利用令牌,一个元素由它的位置(起始索引),长度,以及可选的元素范例来抉择。这些数字存储在与存储令牌沟通的布局中。

再者,范例数组是可选的。若你很容易基于元素的第一个字节或字符确定元素范例,你不必存储元素范例。

元素缓存中标志的要素准确粒度取决于数据被理会,以及需要后头数据处理惩罚的代码。譬喻,假如你实现一个XML理会器,你大概会标志为每个“理会器元素”的开始标签, 属性和竣事标签。

元素缓存(索引)

理会器生成带有指向元数据的索引的元素缓存。该索引标志理会器从数据中获取的元素的位置(起始索引),长度和范例。你可以利用这些索引来会见原始数据。

看一看上文的IndexBuffer代码,你就知道元素缓存每个元素利用9字节;四个字节标志位置,四个本身是令牌长度,一个字节是令牌范例。

你可以淘汰IndexBuffer 的内存耗损。譬喻,假如你知道元素从不会高出65,536字节,那么你可以用短整型数组取代整型来存令牌长度。这将每个元素节减两个字节,使内存耗损低落为每个元素7个字节。

另外,假如知道将理会这些文件长度从不会高出16,777,216字节,你只需要三个字节标识位置(起始索引)。在位置数组中,每一整型第四字节可以生存元素范例,省去了一个范例数组。假如您有少于128的令牌范例,您可以利用7位的令牌范例而不是八个。这使您可以花25位在位置上,这增加了位置范畴最大到33,554,432。假如您令牌范例少于64,您可以布置另一个位给位置,诸如此类。

#p#分页标题#e#

VTD-XML实际上会将所有这些信息压缩成一个Long型,以节减空间。处理惩罚速度会有损失,因为特另外位操纵收拾单独字段到单个整型或long型中,不外你可以节减一些内存。总而言之,这是一个衡量。

元素导航组件

元素导航组件辅佐正在处理惩罚数据的代码会见元素缓存。务必记着,一个语义工具或元素(如XML元素)大概包罗多个理会器元素。为了利便会见,您可以建设一个元素导航器工具,可以在语义工具级别会看法析器元素。譬喻,一个XML元素导航器组件可以通过在起始标志和到起始标志来会见元素缓存。

利用元素导航组件是你的自由。假如要实现一个理会器在单个项目中的利用,你可以要跳过它。可是,假如你正在跨项目中重用它,或作为开源项目宣布它,你大概需要添加一个元素导航组件,这取决于如何会见已理会数据的巨大度。

案例进修:一个JSON理会器

为了让索引叠加理会器设计更清晰,我基于索引叠加理会器设计用Java实现了一个小的JSON理会器。你可以在GitHub上找到完整的代码。

JSON是JavaScript Object Notation的简写。JSON是一种风行的数据名目,基于AJAX来互换Web处事器和欣赏器之间的数据,Web欣赏器已经内置了JSON理会为JavaScript工具的原生支持。后文,我将假定您熟悉JSON。

如下是一个JSON简朴示例:

{ "key1" : "value1" , "key2" : "value2" , [ "valueA" , "valueB" , "valueC" ] } 

JSON阐明器将JSON字符串解析为如命令牌:

实现高性能Java剖析器

这里下划线用于强调每个令牌的长度。

阐明器也能判定每个令牌的根基范例。如下是同一个JSON示例,只是增加了令牌范例:

实现高性能Java剖析器

留意令牌范例不是语义化的。它们只是说明根基令牌范例,而不是它们代表什么。

理会器表明根基令牌范例,并利用语义化范例来替换它们。如下示例是同一个JSON示例,只是由语义化范例(理会器元素)取代:

实现高性能Java剖析器

一旦理会器完成了上述JSON理会,你将有一个索引,包括上面打标志元素的位置,长度和元素范例。你可以会见索引从JSON抽取你需要的数据。

在GitHub库中的实现包括两个JSON理会器。个中一个支解理会进程为JsonTokenizer和JsonParser(如本文前面所述),以及一个为JsonParser2团结阐明息争析进程为一个阶段,一个类。JsonParser2速度更快,但更难领略。因此,我会在下面的章节快速先容一下在JsonTokenizer和JsonParser类,但会跳过JsonParser2。

(本文第一个版本有读者指出,从该指数叠加阐明器的输出是不是难于从原始数据缓冲区中提取数据。正如前面提到的,这就是添加一个元素导航组件的原因。为了说明这样的元素导航组件的道理,我已经添加了JsonNavigator类。稍后,我们也将快速欣赏一下这个类。)

#p#副标题#e#

JsonTokenizer.parseToken()要领

为了先容阐明息争析进程实现道理,我们看一下JsonTokenizer 和JsonParser 类的焦点代码部门。提醒,完整代码可以在GitHub 会见 。

如下是JsonTokenizer.parseToken()要领,理会数据缓存的下一个索引:

public void parseToken() {
       skipWhiteSpace();
       this.tokenBuffer.position[this.tokenIndex] = this.dataPosition;
       switch (this.dataBuffer.data[this.dataPosition]) {
         case '{': {
           this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_CURLY_BRACKET_LEFT;
         }
         break;
         case '}': {
           this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_CURLY_BRACKET_RIGHT;
         }
         break;
         case '[': {
           this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_SQUARE_BRACKET_LEFT;
         }
         break;
         case ']': {
           this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_SQUARE_BRACKET_RIGHT;
         }
         break;
         case ',': {
           this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_COMMA;
         }
         break;
         case ':': {
           this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_COLON;
         }
         break;
         case '"': { parseStringToken(); } break;
         case '0':
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9': {
           parseNumberToken();
           this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_NUMBER_TOKEN;
         }
         break;
         case 'f': {
           if (parseFalse()) {
             this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_BOOLEAN_TOKEN;
           }
         }
         break;
         case 't': {
           if (parseTrue()) {
               this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_BOOLEAN_TOKEN;
           }
         }
         break;
         case 'n': {
           if (parseNull()) {
             this.tokenBuffer.type[this.tokenIndex] = TokenTypes.JSON_NULL_TOKEN;
           }
         }
         break;
        }
        this.tokenBuffer.length[this.tokenIndex] = this.tokenLength;
     } 

#p#分页标题#e#

如你所见,代码相当简捷。首先,skipWhiteSpace()挪用跳过存在于当前位置的数据中的空格。接着,当前令牌(数据缓存的索引)的位置存于tokenBuffer 。第三,查抄下一个字符,并按照字符是什么(它是什么样令牌)来执行switch-case 布局。最后,生存当前令牌的令牌长度。

这简直是阐明一个数据缓冲区的完整进程。请留意,一旦一个字符串索引开始被发明,该阐明器挪用parseStringToken()要领,通过扫描的数据,直到字符串令牌末了。这比试图处理惩罚parseToken()要领内所有逻辑执行更快,也更容易实现。

JsonTokenizer 内要领的其余部门只是帮助parseToken()要领,可能移动数据位置(索引)到下一个令牌(当前令牌的第一个位置),诸如此类。

#p#副标题#e#

JsonParser.parseObject()要领

JsonParser类主要的要领是parseObject()要领,它主要处理惩罚从JsonTokenizer获得令牌的范例,并试图按照上述范例的输入数据找到JSON工具中。

如下是parseObject() 要领:

private void parseObject(JsonTokenizer tokenizer) {
       assertHasMoreTokens(tokenizer);
       tokenizer.parseToken();
       assertThisTokenType(tokenizer.tokenType(), TokenTypes.JSON_CURLY_BRACKET_LEFT);
       setElementData(tokenizer, ElementTypes.JSON_OBJECT_START);
       tokenizer.nextToken();
       tokenizer.parseToken();
       byte tokenType = tokenizer.tokenType();
       while (tokenType != TokenTypes.JSON_CURLY_BRACKET_RIGHT) {
          assertThisTokenType(tokenType, TokenTypes.JSON_STRING_TOKEN);
          setElementData(tokenizer, ElementTypes.JSON_PROPERTY_NAME);
          tokenizer.nextToken();
          tokenizer.parseToken();
          tokenType = tokenizer.tokenType();
          assertThisTokenType(tokenType, TokenTypes.JSON_COLON);
          tokenizer.nextToken();
          tokenizer.parseToken();
          tokenType = tokenizer.tokenType();
          switch (tokenType) {
             case TokenTypes.JSON_STRING_TOKEN: {
               setElementData(tokenizer, ElementTypes.JSON_PROPERTY_VALUE_STRING);
             }
             break;
             case TokenTypes.JSON_STRING_ENC_TOKEN: {
               setElementData(tokenizer, ElementTypes.JSON_PROPERTY_VALUE_STRING_ENC);
             }
             break;
             case TokenTypes.JSON_NUMBER_TOKEN: {
               setElementData(tokenizer, ElementTypes.JSON_PROPERTY_VALUE_NUMBER);
             }
             break;
             case TokenTypes.JSON_BOOLEAN_TOKEN: {
               setElementData(tokenizer, ElementTypes.JSON_PROPERTY_VALUE_BOOLEAN);
             }
             break;
             case TokenTypes.JSON_NULL_TOKEN: {
               setElementData(tokenizer, ElementTypes.JSON_PROPERTY_VALUE_NULL);
             }
             break;
             case TokenTypes.JSON_CURLY_BRACKET_LEFT: {
               parseObject(tokenizer);
             }
             break;
             case TokenTypes.JSON_SQUARE_BRACKET_LEFT: {
               parseArray(tokenizer);
             }
             break;
          }
          tokenizer.nextToken();
          tokenizer.parseToken();
          tokenType = tokenizer.tokenType();
          if (tokenType == TokenTypes.JSON_COMMA) {
             tokenizer.nextToken(); //skip , tokens if found here.
             tokenizer.parseToken();
             tokenType = tokenizer.tokenType();
          }
       }
       setElementData(tokenizer, ElementTypes.JSON_OBJECT_END); } 
     }

parseObject()要领但愿看到一个左花括号({),后跟一个字符串标志,一个冒号和另一个字符串令牌或数组的开头([])或另一个JSON工具。当JsonParser从JsonTokenizer获取这些令牌时,它存储开始,长度和这些令牌在本身elementBuffer中的语义。然后,数据处理惩罚代码可以欣赏这个elementBuffer后,从输入数据中提取任何需要的数据。

看过JsonTokenizer和JsonParser类的焦点部门后能让我们领略阐明息争析的事情方法。为了充实领略代码是如何运作的,你可以看看完整的JsonTokenizer和JsonParser实现。他们每个都不到200行,所以它们应该是易于领略的。

JsonNavigator组件

#p#分页标题#e#

JsonNavigator是一个元素会见组件。它可以辅佐我们会见 JsonParser 和JsonParser2建设的元素索引。两个组件发生的索引是沟通的,所以来自两个组件的任何一个索引都可以。如下是代码示例:

JsonNavigator jsonNavigator = new JsonNavigator(dataBuffer, elementBuffer); 

一旦JsonNavigator建设,您可以利用它的导航要领,next(),previous()等等。你可以利用asString(),asInt()和asLong()来提取数据。你可以利用isEqualUnencoded(String)来较量在数据缓冲器中元素的常量字符串。

利用JsonNavigator类看起来很是雷同于利用GSON流化API。可以较量一下AllBenchmarks类的gsonStreamBuildObject(Reader)要领,和JsonObjectBuilder类parseJsonObject(JsonNavigator)要领。

他们看起来很相似,不是么? 只是,parseJsonObject()要领可以或许利用JsonNavigator的一些优化(在本文后头接头),像数组中根基元素计数,以及对JSON字段名称更快的字符串较量。

#p#副标题#e#

基准化阐明

VTD-XML对StAX,SAX和DOM理会器等XML理会器做了的遍及的基准化较量测试。在焦点机能上,VTD-XML赢得了他们。

为了对索引叠加理会器的机能成立一些信任依据,我已经参考GSON实现了我的JSON理会器。本文的第一个版本只测算了理会一个JSON文件的速度与通过GSON反射结构工具。基于读者的意见,我此刻已经扩大了基准,基于四种差异的模式来测算GSON:

1、会见JSON文件所有元素,但不做任何数据处理惩罚。

2、会见JSON文件所有元素,并成立一个JSONObject。

3、理会JSON文件,并构建了一个Map工具。

4、理会JSON文件,并利用反射它成立一个JSONObject。

请记着,GSON是一个高质量的产物,颠末尾很好的测试,也具有精采的错误陈诉等。只有我的JSON理会器是在观念验证级别。基准测试只是用来得到机能上的差别指标。他们不是最终的数据。也请阅读下文的基准接头。

如下是一些基准布局化组织的细节:

· 为了均衡JIT,只管减小一次性开销,诸如此类。JSON输入完成1000万次的小文件理会,100万次中等文件和大文件。

· 基准化测试别离反复三个差异范例的文件, 看看理会器如何做小的,中等和大文件。上述文件范例巨细别离为58字节,783字节和1854字节。这意味着先迭代1000万次的一个小文件,举办测算。然后是中等文件,最后在大文件。上述文件存于GitHub库的数据目次中。

· 在理会和测算前,文件完全装载进内存中。这样理会耗时不包括装载时间。

· 1000万次迭代(或100万次迭代)测算都是在本身的历程中举办。这意味着,每个文件在单独的历程举办理会。一个进程运行一次。每个文件都测算3次。理会文件1000万次的进程启动和遏制3次。流程是顺序举办的,而不是并行。

如下是毫秒级的执行时间数据:

实现高性能Java剖析器

如你所见,索引叠加实现(JsonParser和JsonParser2)比Gson更快。下面我们将接头一下发生上述功效的原因的猜测。

#p#副标题#e#

机能阐明

GSON Streaming API并非更快的主要原因是当遍历时所有数据都从流中抽取,纵然不需要这些数据。每一个令牌酿成一个string,int,double等,存在耗损。这也是为什么用Gson streaming API理会JSON文件和构建JsonOject和会见元素自己是一样快。 独一增加的显式时间是JsonObject内部的JsonObject和数组的实例化。

数据获取不能表明这一切,尽量,利用JsonParser2构建一个JSONObject比利用Gson streaming API构建JSONObject险些快两倍。如下说明白一些我看到的索引叠加理会器比流式理会器的机能优势:

首先,假如你看一下小的和大的文件的测试数据,每一次理会式GSON都存在一次性开销。 JsonParser2+ JsonParser和GSON基准测试间的机能差别在小的文件上更明明。大概原因是theCharArrayReader建设,或雷同的工作。也大概是GSON内部的某项处理惩罚。

第二,索引叠加理会器可以答允你节制你想抽取的数据量。这个让你更细粒度的节制理会器的机能。

第三, 若一个字符串令牌含有需要手动从UTF-8转换为UTF-16的转义字符(如“\”\ t\ N \ R“),JsonParser和JsonParser2在阐明时可以或许识别。假如一个字符串令牌不包括转义字符,JsonNavigator可以用一个比它们更快的字符串建设机制。

第四,JsonNavigator可以或许让数据缓冲区中的数据的字符串较量更快。 当你需要查抄字段名是否便是常量名时,很是利便。利用Gson’s streaming API,你将需将字段名抽取为一个String工具,并较量常量字符串和String工具。JsonNavigator可以直接较量常量字符串和数据缓冲区中的字符,而无需先建设一个String工具。这可以节减一个String工具的实例化,并从数据缓冲区中的数据复制到一个String工具的时间,它是仅用于较量(如查抄JSON字段名称是否便是“key”或“name”或其它)。JsonNavigator利用方法如下所示:

if(jsonNavigator.isEqualUnencoded("fieldName")) { } 

#p#分页标题#e#

第五,JsonNavigator可以在其索引向前遍历,计数包括原始值(字符串,数字,布尔值,空值等,但不包括工具或嵌套数组)数组中的元素数量。当你不知道数组包括有几多个元素,我们凡是抽取元素并把它们放到一个List中。一旦你碰着数组竣事的标志,将List转成数组。这意味着构建了非须要的List工具。另外,纵然该数组包括原始值,如整数或布尔值,所有抽取的数据也必需要插入到List工具。抽取数值插入List时举办了不须要的工具建设(至少是不须要的自动装箱)。再次,建设基本值数组时,所有的工具都必需再次转换成原始范例,然后插入到数组中。如下所示是Gson streaming API事情代码:

List<Integer> elements = new ArrayList<Integer>();
reader.beginArray();
while (reader.hasNext()) {
   elements.add(reader.nextInt());
}
reader.endArray();
int[] ints = new int[elements.size()];
for (int i = 0; i < ints.length; i++) {
   ints[i] = elements.get(i);
}

当知道数组包括的元素数时,我们可以当即建设最终的Java数组,然后将原始值直接放入数组。在插入数值到数组时,这节减了List实例化和构建,原始值自动装箱和工具转换到原始值的时间。如下所示是利用JsonNavigator成果沟通的代码:

int[] ints = new int[jsonNavigator.countPrimitiveArrayElements()];
for (int i = 0, n = ints.length; i < n; i++) {
   ints[i] = jsonNavigator.asInt();
   jsonNavigator.next();
}

纵然方才从JSON数组构建List工具,知道元素的个数可以让你从一开始就能正确的实例化一个ArrayList工具。这样,你就制止了在到达预设阈值时需动态调解ArrayList巨细的贫苦。如下是示例代码:

List<String> strings = new ArrayList<String>(jsonNavigator.countPrimitiveArrayElements());
jsonNavigator.next(); // skip over array start. 
while (ElementTypes.JSON_ARRAY_END != jsonNavigator.type()) {
   strings.add(jsonNavigator.asString());
   jsonNavigator.next();
}
jsonNavigator.next(); //skip over array end.

第六,当需会见原始数据缓冲区时,可以在许多处所用ropes取代String工具。一个rope是一个含有char数组引用的一个字符串令牌,有起始位置和长度。可以举办字符串较量,就像一个字符串复制rope等。某些操纵大概用rope要比字符串工具快。因为不复制原始数据,它们还占用更少的内存。

第七,假如需要做许多往返的数据会见,您可以建设更高级的索引。 VTD-XML中的索引包括元素的缩进条理,以及同一层的下一个元素(下一个同级)的引用,带有更高缩进层的第一个元素(初始元素),等等。这些都是增加到线性理会器元素索引顶部的整型索引。这种特另外索引可以让已理会数据的遍历速度更快。

#p#副标题#e#

机能和错误陈诉

若看看JsonParser和JsonParser2代码,你将看到更快的JsonParser2比JsonParser更糟糕的错误陈诉。当阐明息争析阶段一分为二时,精采的数据验证和错误陈诉更易于实现。

凡是环境下,这种差别将触发争论,在理会器的实现举办取舍时,优先思量机能照旧错误陈诉。然而,在索引叠加理会器中,这一接头是没有须要的。

因为原始数据始终以其完整的形式存在于内存中,你可以同时具有快和慢的理会器理会沟通的数据。您可以快速启动快的理会器,若理会失败,您可以利用较慢的理会器来检测个中输入数据中的错误位置。当快的理会器失败时,只要将原始数据交给较慢的理会器。基于这种方法,你可以得到两个理会的利益。

基准阐明

基于数据(GSON)建设的工具树与仅标识在数据中找到的数据索引举办较量,而没有接头较量的标的,这是不公正的较量。

在应用措施内部理会文件凡是需要如下步调:

实现高性能Java剖析器

首先是数据从硬盘可能网络上装载。接着,解码数据,譬喻从UTF-8到UTF-16。第三步,理会数据。第四步,处理惩罚数据。

#p#分页标题#e#

为了只丈量原始的理会器速度, 我预装载待理会的文件到内存。 该基准测试的代码没有以任何方法处理惩罚数据。尽量该基准化测试只是测试基本的理会速度,在运行的应用措施中,机能差别并没有转化成机能显著提高。如下是原因:

流式理会器老是能在所有数据装载进内存前开始理会数据。我的JSON理会器此刻实现版本不能这样做。这意味着纵然它在基本理会基准上更快,在现实运行的应用措施中,我的理会器必需期待数据装载,这将减慢整体的处理惩罚速度。如下图说明:

实现高性能Java剖析器

为了加快整体理会速度,你很大概修改我的理会器为数据装载时即可以理会数据。可是很大概会减慢根基理会机能。但整体速度仍大概更快。

另外,通过在执行的基准测试之前数据预加载到内存中,我也跳过数据解码步调。数据从UTF-8转码为UTF-16是也存在耗损。在现实应用措施中,你不行以跳过这一步。每个待理会的文件来必需要解码。这是所有理会器都要支持的一点。流式理会器可以在读数据时举办解码。索引叠加阐明器也可以在读取数据到缓冲区时举办解码。

VTD-XML 和Jackson (另一个JSON理会器)利用另一种技能。它们不会解码所有的原始数据。相反,它们直接在原始数据长举办阐明,消费各类数据名目,如(ASCII,UTF-8等)。这可以节减昂贵的解码步调,解码要利用相当巨大阐明器。

一般来说,要想知道谁人理会器在你的应用措施更快,需要基于你真实需要理会的数据的基准长举办全量测试。

索引叠加理会器一般接头

我听到的一个阻挡索引叠加阐明器的论点是,要可以或许指向原始数据,而不是将其抽取到一个工具树,理会时保持所有数据在内存中是须要的。在处理惩罚大文件时,这将导致内存耗损暴增。

一般来说,流式阐明器(如SAX或StAX)在理会大文件时将整个文件存入内存。然而,只有文件中的数据可以以更小的块举办理会和处理惩罚,每个块都是独立举办处理惩罚的,这种说法才是对的。譬喻,一个大的XML文件包括一列元素,个中每一个元素都可以单独被理会和处理惩罚(如日志记录列表)。假如数据能以独立的块举办理会,你可以实现一个事情精采的索引叠加理会器。

假如文件不能以独立块举办理会,你仍然需要提取须要的信息到一些布局,这些布局可觉得处理惩罚后头块的代码举办会见。尽量利用流式理会器可以做到这一点,你也可以利用索引叠加理会器举办处理惩罚。

从输入数据中建设工具树的理会器凡是会耗损比原数据巨细的工具树更多的内存。工具实例相关联的内存开销,加上需要保持工具之间的引用的特别数据,这是主要原因。

另外,因为所有的数据都需要同时在内存中,你需要理会前分派一个数据缓冲区,大到足以容纳所有的数据。可是,当你开始理会它们时,你并不知道文件巨细,如何办呢?

如果你有一个网页应用措施(如Web处事,可能处事端应用),用户利用它上传文件。你不行能知道文件巨细,所以开始理会前无法分派符合的缓存给它。基于安详思量,你应该老是配置一个最大答允文件巨细。不然,用户可以通过上传超大文件让你的应用瓦解。可能,他们大概甚至写一个措施,伪装成上传文件的欣赏器,并让该措施不断地向处事器发送数据。您可以分派一个缓冲区适合所答允的最大文件巨细。这样,你的缓冲区不会因有效文件耗光。假如它耗光了空间,那说明你的用户已经上传了过大的文件。

 

    关键字:

天才代写-代写联系方式