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