On 20.07.2007 06:56:21 Andreas L Delmelle wrote:
> On Jul 19, 2007, at 00:36, Andreas L Delmelle wrote:
> 
> >
> > On Jul 18, 2007, at 23:18, Jeremias Maerki wrote:
> > <snip />
> >> - One of the easiest candidates for another flyweight is probably
> >> CommonHyphenation (56K instances, 2.3MB in my example). The few  
> >> member
> >> variables could probably just be concatenated to a String (to be  
> >> used as
> >> the key).
> >
> > Interesting idea, will look into that asap.
> 
> FWIW:
> Looked a bit closer at this, and it suddenly struck me that all the  
> base Property types, apart from CharacterProperty which I overlooked  
> as a possible candidate, were already cached:
> 
> StringProperty -> language, country, script
> NumberProperty -> hyphenation-push/remain-character-count
> EnumProperty -> hyphenate
> 
> CharacterProperty(*) -> hyphenation-character
> 
> (*) now also added, see http://svn.apache.org/viewvc?view=rev&rev=557814
> 
> This means we currently end up in the strange situation where  
> different/separate CommonHyphenation instances are generated from  
> identical sets of base Property instances.

Raises the question for me if for properties without dynamic context-based
evaluation the property evaluation could be streamlined to directly
return the primitive values instead of simple container objects like
NumberProperty. throw new NotEnoughTimeRightNowException();

> Maybe the CommonHyphenation bundle could store references to the  
> original properties themselves instead of duplicating their content/ 
> value and storing them as primitives? By itself, this should be  
> roughly the same in terms of overall memory consumption: replacement  
> of some primitives with references.
> 
> In that case, one of the additional benefits of the individual  
> Property caching is that you can now actually avoid calls to  
> StringProperty.equals() in the rest of the code. "identity" means the  
> same as "equality" here, so the fastest possible implementation for  
> CommonHyphenation.equals() would then come to look like:
> 
> public final class CommonHyphenation {
> ...
> public final StringProperty language;
> public final StringProperty script;
> public final StringProperty country;
> public final EnumProperty hyphenate;
> ...
> public boolean equals(Object obj) {
>    if (obj == this) {
>      return true;
>    }
>    if (obj instanceof CommonHyphenation) {
>      CommonHyphenation ch = (CommonHyphenation) obj;
>      return (ch.language == this.language
>           && ch.script == this.script
>           && ch.country == this.country
>           && ch.hyphenate == this.hyphenate
>           && ...)
>    }
>    return false;
> }
> 
> One thing that cannot be avoided is the multiple calls to  
> PropertyList.get() to get to the properties that are needed to  
> perform the check for a flyweight bundle. Maybe the initial  
> assignments can be moved into the getInstance() method, so they  
> become part of the static code. getInstance() would get a  
> PropertyList as argument, while the private constructor signature is  
> altered to accept all the base properties as parameters.
> 
> The key in the Map could be a composite String, but could also again  
> be the CommonHyphenation itself, if a decent hashCode()  
> implementation is added.
> The benefit of using the instance itself is that the key in a  
> WeakHashMap is automatically released after the last object referring  
> to it has been cleared. Using a key other than the instance itself  
> would make WeakHashMap unusable, since the keys are in that case not  
> referenced directly by any object. The key cannot be embedded in the  
> instance itself, since that would prevent the entire entry from ever  
> being released...
> 
> The properties themselves being immutable and final, I guess it does  
> no harm to expose them as public members. Only a handful of places in  
> TextLM and LineLM would need a slight adjustment to compensate for  
> the lost getString() and getEnum() conversions. Maybe for  
> convenience, if really needed, accessors could be added like:
> 
> public String language() {
>    return language.getString();
> }
> ...
> public boolean hyphenate() {
>    return (hyphenate.getEnum() == EN_TRUE);

Well, I'd prefer Bean-style getters, i.e. getLanguage(),
isHyphenationEnabled()

> 
> Opinions?
> For the interested parties: full CommonHyphenation below, following  
> roughly the same principles as the Property caching.

"hash" should probably be transient here because it's a cached value.

> Cheers
> 
> Andreas
> 
> --- Sample code ---
> public final class CommonHyphenation {
> 
>      private static final Map cache =  
> java.util.Collections.synchronizedMap(
>                                          new java.util.WeakHashMap());
> 
>      private int hash = 0;
> 
>      /** The "language" property */
>      private final StringProperty language;
> 
>      /** The "country" property */
>      private final StringProperty country;
> 
>      /** The "script" property */
>      private final StringProperty script;
> 
>      /** The "hyphenate" property */
>      private final EnumProperty hyphenate;
> 
>      /** The "hyphenation-character" property */
>      private final CharacterProperty hyphenationCharacter;
> 
>      /** The "hyphenation-push-character-count" property */
>      private final NumberProperty hyphenationPushCharacterCount;
> 
>      /** The "hyphenation-remain-character-count" property*/
>      private final NumberProperty hyphenationRemainCharacterCount;
> 
>      /**
>       * Construct a CommonHyphenation object holding the given  
> properties
>       *
>       */
>      private CommonHyphenation(StringProperty language,
>                                StringProperty country,
>                                StringProperty script,
>                                EnumProperty hyphenate,
>                                CharacterProperty hyphenationCharacter,
>                                NumberProperty  
> hyphenationPushCharacterCount,
>                                NumberProperty  
> hyphenationRemainCharacterCount) {
>          this.language = language;
>          this.country = country;
>          this.script = script;
>          this.hyphenate = hyphenate;
>          this.hyphenationCharacter = hyphenationCharacter;
>          this.hyphenationPushCharacterCount =  
> hyphenationPushCharacterCount;
>          this.hyphenationRemainCharacterCount =  
> hyphenationRemainCharacterCount;
>      }
> 
>      /**
>       * Gets the canonical <code>CommonHyphenation</code> instance  
> corresponding
>       * to the values of the related properties present on the given
>       * <code>PropertyList</code>
>       *
>       * @param propertyList  the <code>PropertyList</code>
>       */
>      public static CommonHyphenation getInstance(PropertyList  
> propertyList) throws PropertyException {
>          StringProperty language =
>              (StringProperty) propertyList.get(Constants.PR_LANGUAGE);
>          StringProperty country =
>              (StringProperty) propertyList.get(Constants.PR_COUNTRY);
>          StringProperty script =
>              (StringProperty) propertyList.get(Constants.PR_SCRIPT);
>          EnumProperty hyphenate =
>              (EnumProperty) propertyList.get(Constants.PR_HYPHENATE);
>          CharacterProperty hyphenationCharacter =
>              (CharacterProperty) propertyList.get 
> (Constants.PR_HYPHENATION_CHARACTER);
>          NumberProperty hyphenationPushCharacterCount =
>              (NumberProperty) propertyList.get 
> (Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT);
>          NumberProperty hyphenationRemainCharacterCount =
>              (NumberProperty) propertyList.get 
> (Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT);
> 
>          CommonHyphenation instance = new CommonHyphenation(
>                                  language,
>                                  country,
>                                  script,
>                                  hyphenate,
>                                  hyphenationCharacter,
>                                  hyphenationPushCharacterCount,
>                                  hyphenationRemainCharacterCount);
> 
>          Object cachedInstance = cache.get(instance);
>          if (cachedInstance == null) {
>              cache.put(instance, instance);
>          } else {
>              instance = (CommonHyphenation) cachedInstance;
>          }
>          return instance;
> 
>      }
> 
>      /** @return the "lanuage" property as a String */
>      public String language() {
>          return language.getString();
>      }
> 
>      /** @return the "country" property as a String */
>      public String country() {
>          return country.getString();
>      }
> 
>      /** @return the "script" property as a String */
>      public String script() {
>          return script.getString();
>      }
> 
>      /** @return the "hyphenate" property as a boolean */
>      public boolean hyphenate() {
>          return (hyphenate.getEnum() == Constants.EN_TRUE);
>      }
> 
>      /** @return the "hyphenation-character" property as a char */
>      public char hyphenationCharacter() {
>          return hyphenationCharacter.getCharacter();
>      }
> 
>      /** @return the "hyphenation-push-character-count" property as  
> an int */
>      public int hyphenationPushCharacterCount() {
>          return hyphenationPushCharacterCount.getNumber().intValue();
>      }
> 
>      /** @return the "hyphenation-remain-character-count" property as  
> an int */
>      public int hyphenationRemainCharacterCount() {
>          return hyphenationRemainCharacterCount.getNumber().intValue();
>      }
> 
>      /** [EMAIL PROTECTED] */
>      public boolean equals(Object obj) {
>          if (obj == this) {
>              return true;
>          }
>          if (obj instanceof CommonHyphenation) {
>              CommonHyphenation ch = (CommonHyphenation) obj;
>              return (ch.language == this.language
>                      && ch.country == this.country
>                      && ch.script == this.script
>                      && ch.hyphenate == this.hyphenate
>                      && ch.hyphenationCharacter ==  
> this.hyphenationCharacter
>                      && ch.hyphenationPushCharacterCount ==  
> this.hyphenationPushCharacterCount
>                      && ch.hyphenationRemainCharacterCount ==  
> this.hyphenationRemainCharacterCount);
>          }
>          return false;
>      }
> 
>      /** [EMAIL PROTECTED] */
>      public int hashCode() {
>          if (hash == 0) {
>              int hash = 7;
>              hash = 31 * hash + (language == null ? 0 :  
> language.hashCode());
>              hash = 31 * hash + (script == null ? 0 : script.hashCode 
> ());
>              hash = 31 * hash + (country == null ? 0 :  
> country.hashCode());
>              hash = 31 * hash + (hyphenate == null ? 0 :  
> hyphenate.hashCode());
>              hash = 31 * hash +
>                  (hyphenationCharacter == null ? 0 :  
> hyphenationCharacter.hashCode());
>              hash = 31 * hash +
>                  (hyphenationPushCharacterCount == null ? 0 :  
> hyphenationPushCharacterCount.hashCode());
>              hash = 31 * hash +
>                  (hyphenationRemainCharacterCount == null ? 0 :  
> hyphenationRemainCharacterCount.hashCode());
>          }
>          return hash;
>      }
> 
> }



Jeremias Maerki

Reply via email to