3

这可以被视为一般 Java 问题,但为了更好地理解,我使用 Lucene 作为示例。

您可以在 Lucene 中使用不同的 Tokenizer 来标记文本。有主要的抽象 Tokenizer 类,然后是许多不同的扩展它的类。TokenFilter 也是一样。

现在,似乎每次您想要索引文档时,都会创建一个新的 Tokenizer。问题是,既然 Tokeinzer 只是一个实用程序类,为什么不让它成为静态的呢?例如,将所有字母转换为小写的 Tokenizer 可以有一个静态方法,该方法对它获得的每个输入都执行此操作。为我们要索引的每段文本创建一个新对象有什么意义?

值得一提的是——Tokeinzer 有一个私有字段,其中包含它接收到的用于标记化的输入。我只是不明白为什么我们需要以这种方式存储它,因为对象在标记化过程结束后立即被销毁并返回新的标记化文本。我唯一能想到的可能是多线程访问?

谢谢!

4

2 回答 2

6

现在,似乎每次要索引文档时,都会创建一个新的 Tokenizer

这不是真的,调用了 Analyzer.reusableTokenStream 方法,它不仅重用了一个 Tokenizer,还重用了整个链(TokenFilters 等)。见http://lucene.apache.org/java/3_0_0/api/core/org/apache/lucene/analysis/Analyzer.html#reusableTokenStream(java.lang.String , java.io.Reader)

值得一提的是——Tokeinzer 有一个私有字段,其中包含它接收到的用于标记化的输入。我只是不明白为什么我们需要以这种方式存储它,因为对象在标记化过程结束后立即被销毁并返回新的标记化文本。我唯一能想到的可能是多线程访问?

如前所述,整个分词器和分词过滤器链可跨文档重用。所以它们的所有属性都被重用了,但重要的是要注意属性是跨链共享的(例如,所有 Tokenizer 和 TokenFilters 的 Attribute 引用都指向相同的实例)。这就是为什么在标记器中调用 clearAttributes() 来重置所有属性至关重要的原因。

例如,空白标记器在其 ctor 中添加对 TermAttribute 的引用,并由 LowerCaseFilter 包装,后者也在其 ctor 中添加对 TermAttribute 的引用。这两个 TermAttributes 都指向相同的底层 char[]。当一个新文档被处理时,Analyzer.reusableTokenStream 被调用,它返回之前文档中使用的相同的 TokenStream 链(在本例中 Whitespace 用 LowerCaseFilter 包裹)。调用 reset(Reader) 方法,将分词器的输入重置为新的文档内容。最后,在整个流上调用 reset(),这会重置前一个文档的任何内部状态,并处理内容,直到 incrementToken() 返回 false。

于 2011-01-14T11:43:52.517 回答
0

在做一些复杂的事情(比如用 Lucene 索引文档)时,不要担心在这里和那里创建一个类的实例。在标记化和索引过程中可能会创建很多很多对象。当进程完成时比较丢弃的对象的剩余垃圾时,再有一个标记器实例实际上什么都不是。如果您不相信我,请获取配置文件并查看对象创建计数。

于 2011-01-14T11:53:01.303 回答