Lucene中AttributeSource作为TokenStream父类的原因

Lucene中AttributeSource作为TokenStream父类的原因lucene3.0lucene中有如下的类层次:org.apache.lucene.util.AttributeSourceorg.apache.lucene.analysis.TokenStream(implementsjava.io.Closeable)org.apache.lucene.analysis.NumericTokenStreamorg.apache…

大家好,又见面了,我是你们的朋友全栈君。

lucene3.0

lucene中有如下的类层次:

org.apache.lucene.util.AttributeSource

  • org.apache.lucene.analysis.TokenStream (implements java.io.Closeable)
    • org.apache.lucene.analysis.NumericTokenStream
    • org.apache.lucene.analysis.TeeSinkTokenFilter.SinkTokenStream
    • org.apache.lucene.analysis.TokenFilter
      • org.apache.lucene.analysis.ASCIIFoldingFilter
      • org.apache.lucene.analysis.CachingTokenFilter
      • org.apache.lucene.analysis.ISOLatin1AccentFilter
      • org.apache.lucene.analysis.LengthFilter
      • org.apache.lucene.analysis.LowerCaseFilter
      • org.apache.lucene.analysis.PorterStemFilter
      • org.apache.lucene.analysis.StopFilter
      • org.apache.lucene.analysis.TeeSinkTokenFilter
    • org.apache.lucene.analysis.Tokenizer
      • org.apache.lucene.analysis.CharTokenizer
        • org.apache.lucene.analysis.LetterTokenizer
          • org.apache.lucene.analysis.LowerCaseTokenizer
        • org.apache.lucene.analysis.WhitespaceTokenizer
      • org.apache.lucene.analysis.KeywordTokenizer

从上可以看出 AttributeSource是作为TokenStream 的超类,为什么要这样呢?

1.首先我们来看AttributeSource的实现:

1.1 成员属性:

—————————————————————————————————————————————————————————————–

private final Map<Class<? extends Attribute>, AttributeImpl> attributes; 
private final Map<Class<? extends AttributeImpl>, AttributeImpl> attributeImpls;

private AttributeFactory factory;

private static final WeakHashMap<Class<? extends AttributeImpl>,LinkedList<WeakReference<Class<? extends Attribute>>>> knownImplClasses;

—————————————————————————————————————————————————————————————–

1.1.1 Attribute:

仅仅是一个空的接口,实现该接口的接口有FlagsAttribute, OffsetAttribute, PayloadAttribute, PositionIncrementAttribute, TermAttribute, TypeAttribute,从他们的命名上可以看出其与lucene项的属性有关联,其实上述接口的确与项的属性有一一对应的关系,这些接口定义了对项的特定属性的规范操作,但仅仅定义了操作的行为,具体的属性的数据却在在AttributeImpl的派生类中定义。

1.1.2 AttributeImpl:

类定义如是:public abstract class AttributeImpl implements Cloneable, Serializable, Attribute

具体的继承该类的派生类有

FlagsAttributeImpl, OffsetAttributeImpl, PayloadAttributeImpl, PositionIncrementAttributeImpl, TermAttributeImpl, Token, TypeAttributeImpl

看FlagsAttributeImpl具体的类定义:public class FlagsAttributeImpl extends AttributeImpl implements FlagsAttribute, Cloneable, Serializable

可见其实现了FlagsAttribute接口,另外看FlagsAttributeImpl的源代码可知,其内还有关于项的标志属性的数据信息,因此我们可以推测:

AttributeImpl是属性的数据信息以及和该属性关联的操作的类,该属性关联的操作放在了Attribute接口中定义,实现是在AttributeImpl中。

1.1.3 综上,我们有结论如下,Token有若干的属性,对每个属性其表达由Attribute和AttributeImpl完成,Attribute定义对该属性的操作,AttributeImpl实现该接口并包含具体的属性数据。

1.1.4

private final Map<Class<? extends Attribute>, AttributeImpl> attributes; 
private final Map<Class<? extends AttributeImpl>, AttributeImpl> attributeImpls;

上述两个成员保存了两种映射关系,AttributeImpl实例对应实现的所有Attribute接口,都可以映射到该AttributeImpl实例,这是第一个映射;第二个映射是AttributeImpl实例对应实现的AttributeImpl抽象类对该AttributeImpl实例的映射。

设计这两个映射关系的目的是在该AttributeSource实例中对每个Attribute和AttributeImpl保证只有一个AttributeImpl实例,换句话说,当用具体Attribute或者具体AttributeImpl获取其对象实例时,不会每次都新建实例,而是首次时建立,其后只返回以前建立的。下面来剖析实现方法;

public void addAttributeImpl(final AttributeImpl att) {

/*

从绿色部分可以看出如果之前已经有att类型的实例被保存在映射中,那么添加该类型实例将失败;

//*/  
    final Class<? extends AttributeImpl> clazz = att.getClass(); 
    if (attributeImpls.containsKey(clazz)) return;

/*

深红色部分是获取该att实现的所有Attribute接口,并保存在foundInterfaces中;

//*/ 
    LinkedList<WeakReference<Class<? extends Attribute>>> foundInterfaces; 
    synchronized(knownImplClasses) { 
      foundInterfaces = knownImplClasses.get(clazz); 
      if (foundInterfaces == null) { 
        // we have a strong reference to the class instance holding all interfaces in the list (parameter “att”), 
        // so all WeakReferences are never evicted by GC 
        knownImplClasses.put(clazz, foundInterfaces = new LinkedList<WeakReference<Class<? extends Attribute>>>()); 
        // find all interfaces that this attribute instance implements 
        // and that extend the Attribute interface 
        Class<?> actClazz = clazz; 
        do { 
          for (Class<?> curInterface : actClazz.getInterfaces()) { 
            if (curInterface != Attribute.class && Attribute.class.isAssignableFrom(curInterface)) { 
              foundInterfaces.add(new WeakReference<Class<? extends Attribute>>(curInterface.asSubclass(Attribute.class))); 
            } 
          } 
          actClazz = actClazz.getSuperclass(); 
        } while (actClazz != null); 
      } 
    }

/*

海军蓝部分是将该att实现的每个Attribute接口与att之间的映射关系添加到Map中;

//*/ 
    // add all interfaces of this AttributeImpl to the maps 
    for (WeakReference<Class<? extends Attribute>> curInterfaceRef : foundInterfaces) { 
      final Class<? extends Attribute> curInterface = curInterfaceRef.get(); 
      assert (curInterface != null) : 
        “We have a strong reference on the class holding the interfaces, so they should never get evicted”; 
      // Attribute is a superclass of this interface 
      if (!attributes.containsKey(curInterface)) { 
        // invalidate state to force recomputation in captureState() 
        this.currentState = null; 
        attributes.put(curInterface, att); 
        attributeImpls.put(clazz, att); 
      } 
    } 
  }

public <A extends Attribute> A addAttribute(Class<A> attClass) { 
    AttributeImpl attImpl = attributes.get(attClass); 
    if (attImpl == null) { 
      if (!(attClass.isInterface() && Attribute.class.isAssignableFrom(attClass))) { 
        throw new IllegalArgumentException( 
          “addAttribute() only accepts an interface that extends Attribute, but ” + 
          attClass.getName() + ” does not fulfil this contract.” 
        ); 
      } 
      addAttributeImpl(attImpl = this.factory.createAttributeInstance(attClass)); 
    } 
    return attClass.cast(attImpl); 
  }

AttributeSource中有一个内部类AttributeFactory类,其中维护了Attribute.Class和AttributeImpl.Class的

对应关系,但是纵观AttributeFactory的实现却并未发现有专用的添加删除操作来维护这种关系,其实其维护只通过一个函数

public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass),当AttributeFactory中的对应关系中有Attribute.Class对应的AttributeImpl.Class映射时,则上述函数直接用AttributeImpl.Class创建AttributeImpl的类实例,并返回,如果不存在这样的映射关系,那么AttributeFactory将使用下述的方法添加这种映射并创建类实例返回。

attClassImplMap.put(attClass, 
                new WeakReference<Class<? extends AttributeImpl>>( 
                  clazz = Class.forName(attClass.getName() + “Impl”, true, attClass.getClassLoader()) 
                  .asSubclass(AttributeImpl.class) 
                );

1.1.5

public <A extends Attribute> A getAttribute(Class<A> attClass):返回在Map中注册的attClass对应的AttributeImpl实例;

public State captureState():返回当前时刻已注册的所有AttributeImpl实例。

2. Lucene中AttributeSource作为TokenStream父类的原因的

2.1 TokenStream的作用是从给入的文本中不断解析出Token,具体的做法是TokenStream有方法incrementToken,每次调用将产生待分析文本的下一个Token,其实incrementToken做的事情就是填充我所关心的若干属性,通过这些属性来反馈分析结果,因此自然而然的一种想法是TokenStream的派生类中有若干的属性成员,每次调用incrementToken都首先清除上一次的属性信息,然后进行分析并填充属性,这样做无可厚非,但是请考虑TokenStream流的嵌套,也就是说嵌套的内层流获取的属性将作为外层流的分析的输入,如果使用上述方法实现TokenStream,则必然嵌套流的每层流都将有自己的属性实例,而层次之间可能会出现同样的属性,也就是说同样的属性实例在流层次中可能会有多个,这样是没有必要的,也就是说对相同的属性在流层次中只有一个实例就可以满足分析的需求了。

2.2 基于2.1读者可能会说嵌套时当外层流与内层流有相同的属性时,可以将外层流的该属性赋内层流的属性引用,这样就可以避免2.1的情况。错误的原因在于,我们在嵌套时,嵌套流的层次关系用户根据自己的需求组合而成的,也就是说外层流往往无法知道自己的内层流会是谁,“将外层流的该属性赋内层流的属性引用”的前提是外层流清楚内层流是谁,因此这样的方法不可行。

2.3 其实上述的获取内层TokenStream中有哪些AttributeImpl的子类实例的方法只需要通过java的反射机制就可以解决,可是为什么lucene还要用AttributeSource这么复杂的一个构建来实现呢?究其原因在于效率的考虑!

当我们将TokenStream所关心的属性抽象的由AttributeSource来管理时,我们在进行流的嵌套时,根据对AttributeSource的分析可知,外层流定义自己关心的属性,并不需要在构造函数中实例化该属性,而是从AttributeSource中获取,如果存在的话,则直接返回实例,否则新建,这样在流嵌套式外层流和内存流共享AttributeSource,也就是说当外层流和内层流都关心某个属性时,内层流首先初始化,此时他将会将该属性注册到AttributeSource中,这样在外层流初始化时将向AttributeSource获取该属性,从而可以保证在流层次中若干层流都关心的属性只有一份实例。

为什么说不用反射实现是基于效率的考虑,这是因为如果使用AttributeSource实现只有在首次注册属性时才会使用反射机制,以后都是直接获取,而如果纯使用反射机制保证流嵌套层次都关心属性实例的唯一性,那么假设有n层嵌套,那么将有n-1次对反射机制的使用,显然AttributeSource的实现方式将有更高的效率。

转载于:https://www.cnblogs.com/zz-boy/archive/2012/08/19/2646202.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/163109.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 利用其它widthStep方法把interest_img的所有像素值增加1[通俗易懂]

    利用其它widthStep方法把interest_img的所有像素值增加1[通俗易懂]#include”stdafx.h”#include#includeintmain(intargc,char**argv){   IplImage*interest_img;   CvRectinterest_rect;   if(argc==7&&((interest_img=cvLoadImage(argv[1],1))!=

    2022年5月6日
    35
  • 如何快速辨识四位数字贴片电阻阻值[通俗易懂]

    如何快速辨识四位数字贴片电阻阻值[通俗易懂]如何快速辨识四位数字贴片电阻阻值最后一位是0,直接读前三位,单位是Ω。例如:4750475Ω最后一位是1,在第一位后面加小数点,单位是kΩ。例如:47514.75kΩ最后一位是2,在第二位后面加小数点,单位是kΩ。例如:475247.5kΩ…

    2022年8月21日
    3
  • 空间流介绍[通俗易懂]

    空间流介绍[通俗易懂]stream是802.11n中的空间流的意思,11n协议中最高支持4空间流。11n协议物理层最核心的技术就是MIMO技术,一般AP设备MIMO都后注1×1,2×2,2×3,3×3等,这两个数字前面一个数字式11nAP的发射天线数量,后面一个数字是11nAP的接受天线数量。11n中所谓的空间流实际就是MIMO空间复用支持的多根天线独立地并行发送由单独编码的信号组成的不同的数据流。无线复用的空间流的数量取决于发射天线的数量。你可以这样简单理解,由于11nAP有多个发射天线,形成多个发射物理信道,与以前的WLA

    2022年7月21日
    37
  • ubuntu安装gcc-6

    ubuntu安装gcc-6原文:https://blog.csdn.net/sinat_23246437/article/details/543426331.安装gcc-6:sudoapt-getupdate&amp;&amp;\sudoapt-getinstallbuild-essentialsoftware-properties-common-y&amp;&amp;\sudoadd…

    2022年7月24日
    18
  • 中文参数乱码问题——js字符串编码

    中文参数乱码问题——js字符串编码jquery.get中文参数问题——js符串编码摘要:使用jquery.get进行ajax请求获取数据是很常见的操作,一般请求参数都为字母,今天发现在参数中使用中文会出现浏览器兼容性问题,现在记录如下。基本使用语法:$(selector).get(url,data,success(response,status,xhr),dataType)参数 描述url 必需。规定将请求

    2022年6月5日
    31
  • 此工作站和主域间的信任关系失败 原因及解决办法[通俗易懂]

    此工作站和主域间的信任关系失败 原因及解决办法[通俗易懂]原因:域控服务器没有客户端的主机名(可能删除了,或重装系统后没添加到域控)处理:在域控上确认客户端主机名是否被禁用,如已禁用,启动即可。转载于:https://www.cnblogs.com/sjdn/p/5923669.html…

    2022年10月19日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号