{"id":3060,"date":"2025-12-24T10:00:28","date_gmt":"2025-12-24T15:00:28","guid":{"rendered":"https:\/\/www.mymiller.name\/wordpress\/?p=3060"},"modified":"2025-12-24T10:00:28","modified_gmt":"2025-12-24T15:00:28","slug":"string-cache","status":"publish","type":"post","link":"https:\/\/www.mymiller.name\/wordpress\/java_extra\/string-cache\/","title":{"rendered":"String Cache"},"content":{"rendered":"\n<p>Sometimes an application will have a large number of Strings. Due to memory we certainly do not want to keep multiple instances of duplicate strings in memory.  A string cache can greatly reduce the number of Strings in memory.  I had a case where I had millions of strings, and greatly needed to reduce the memory used.  Thus a created a StringCache class.  Making use of a WeakHashMap the number of instances of strings in memory.  <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>   \/**\r\n     * Weak has map to allow Strings to be removed when only the cache has a link to\r\n     * them.\r\n     *\/\r\n    private WeakHashMap&lt;String, WeakReference&lt;String>> cache = null;<\/code><\/pre>\n\n\n\n<p>A WeakHashMap will allow the the value to be garbage collected when only the Map contains a reference.  Now the WeakReference is used to eliminate it as a reference.  To cache the a string and get the reference to use we have a simple cache method,<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>   \/**\r\n     * Returns the string matching the string from the cache.\r\n     *\r\n     * @param key String to find cache value.\r\n     * @return Cached instance of the string.\r\n     *\/\r\n    public synchronized String cache(final String key) {\r\n        String cachedString = null;\r\n        if (this.cache.containsKey(key)) {\r\n            final WeakReference&lt;String> ref = this.cache.get(key);\r\n\r\n            if (ref != null) {\r\n                cachedString = ref.get();\r\n                this.collisionCount++;\r\n            }\r\n        }\r\n\r\n        if (cachedString == null) {\r\n            this.cache.put(key, new WeakReference&lt;>(key));\r\n            cachedString = key;\r\n        }\r\n\r\n        return cachedString;\r\n    }<\/code><\/pre>\n\n\n\n<p>We check if the map contains the key if it does we use the reference already stored.  If it does not we then create WeakReference and store it in the Map.  Properties is an object that contains a number of strings.  I created a convenience method to cache Properties.  <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/**\r\n     * Return a Properties object with all strings Cached.\r\n     * @param properties Properties to convert to Cached Strings\r\n     * @return new Properties object with cached strings.\r\n     *\/\r\n    public Properties cacheProperties(Properties properties) {\r\n        Properties cacheProperties = new Properties();\r\n        Set&lt;String> propertyNames = properties.stringPropertyNames();\r\n        propertyNames.forEach(propertyName -> cacheProperties.put(this.cache(propertyName),this.cache(properties.getProperty(propertyName))));\r\n\r\n        return cacheProperties;\r\n    }<\/code><\/pre>\n\n\n\n<p>Below is the entire class <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*\r\n  Copyright 2018 MyMiller Consulting LLC.\r\n  &lt;p>\r\n  Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\r\n  use this file except in compliance with the License.  You may obtain a copy\r\n  of the License at\r\n  &lt;p>\r\n  http:&#47;&#47;www.apache.org\/licenses\/LICENSE-2.0\r\n  &lt;p>\r\n  Unless required by applicable law or agreed to in writing, software\r\n  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\r\n  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the\r\n  License for the specific language governing permissions and limitations under\r\n  the License.\r\n *\/\r\n\/*\r\n\r\n *\/\r\npackage name.mymiller.containers;\r\n\r\nimport java.lang.ref.WeakReference;\r\nimport java.util.Properties;\r\nimport java.util.Set;\r\nimport java.util.WeakHashMap;\r\n\r\n\/**\r\n * String Cache to remove redundant String objects that contain the same data.\r\n *\r\n * @author jmiller\r\n *\/\r\npublic class StringCache {\r\n    \/**\r\n     * Initial capacity of the cache.\r\n     *\/\r\n    private static final int globalInitialCapacity = 65535;\r\n    \/**\r\n     * Initial Load factor to grow the map.\r\n     *\/\r\n    private static final float globalLoadFactor = (float) 0.75;\r\n\r\n    \/**\r\n     * Global static instance of the cache.\r\n     *\/\r\n    private static final StringCache globalInstance = new StringCache();\r\n    \/**\r\n     * Number of collisions the cache has saved.\r\n     *\/\r\n    private int collisionCount = 0;\r\n    \/**\r\n     * Weak has map to allow Strings to be removed when only the cache has a link to\r\n     * them.\r\n     *\/\r\n    private WeakHashMap&lt;String, WeakReference&lt;String>> cache = null;\r\n\r\n    \/**\r\n     * Protected constructor forcing the use of the getInstance methods.\r\n     *\/\r\n    protected StringCache() {\r\n        this.cache = new WeakHashMap&lt;>(StringCache.globalInitialCapacity, StringCache.globalLoadFactor);\r\n    }\r\n\r\n    \/**\r\n     * Protected constructor allowing the configuration of the capacity and load\r\n     * factor\r\n     *\r\n     * @param globalInitialCapacity Initial Capacity of the cache\r\n     * @param globalLoadFactor      Load factor for growth.\r\n     *\/\r\n    protected StringCache(final int globalInitialCapacity, final float globalLoadFactor) {\r\n        this.cache = new WeakHashMap&lt;>(globalInitialCapacity, globalLoadFactor);\r\n    }\r\n\r\n    \/**\r\n     * Method to get a cache with default settings\r\n     *\r\n     * @return Global Cache\r\n     *\/\r\n    public static StringCache getInstance() {\r\n        return StringCache.globalInstance;\r\n    }\r\n\r\n    \/**\r\n     * Returns the string matching the string from the cache.\r\n     *\r\n     * @param key String to find cache value.\r\n     * @return Cached instance of the string.\r\n     *\/\r\n    public synchronized String cache(final String key) {\r\n        String cachedString = null;\r\n        if (this.cache.containsKey(key)) {\r\n            final WeakReference&lt;String> ref = this.cache.get(key);\r\n\r\n            if (ref != null) {\r\n                cachedString = ref.get();\r\n                this.collisionCount++;\r\n            }\r\n        }\r\n\r\n        if (cachedString == null) {\r\n            this.cache.put(key, new WeakReference&lt;>(key));\r\n            cachedString = key;\r\n        }\r\n\r\n        return cachedString;\r\n    }\r\n\r\n    \/**\r\n     * Clears the cache\r\n     *\/\r\n    public synchronized void clear() {\r\n        this.cache.clear();\r\n        this.collisionCount = 0;\r\n    }\r\n\r\n    \/**\r\n     * Determines if the cache contains a String.\r\n     *\r\n     * @param key String to check if it is in the Cache.\r\n     * @return boolean indicating if the String was found.\r\n     * @see WeakHashMap#containsKey(Object)\r\n     *\/\r\n    public synchronized boolean containsKey(final String key) {\r\n        return this.cache.containsKey(key);\r\n    }\r\n\r\n    \/*\r\n     * (non-Javadoc)\r\n     *\r\n     * @see java.name.mymiller.lang.Object#equals(java.name.mymiller.\r\n     * extensions.lang.Object)\r\n     *\/\r\n    @Override\r\n    public synchronized boolean equals(final Object obj) {\r\n        if (this == obj) {\r\n            return true;\r\n        }\r\n        if (obj == null) {\r\n            return false;\r\n        }\r\n        if (!(obj instanceof StringCache)) {\r\n            return false;\r\n        }\r\n        final StringCache other = (StringCache) obj;\r\n        if (this.cache == null) {\r\n            return other.cache == null;\r\n        } else {\r\n            return this.cache.equals(other.cache);\r\n        }\r\n    }\r\n\r\n    \/**\r\n     * Returns a Set view of the strings this cache.\r\n     *\r\n     * @return Set View\r\n     *\/\r\n    public synchronized String&#91;] getCached() {\r\n        return this.cache.keySet().toArray(new String&#91;0]);\r\n    }\r\n\r\n    \/**\r\n     *\r\n     * @return Number of Collisions that have occurred.\r\n     *\/\r\n    public int getCollisionCount() {\r\n        return this.collisionCount;\r\n    }\r\n\r\n    \/*\r\n     * (non-Javadoc)\r\n     *\r\n     * @see java.name.mymiller.lang.Object#hashCode()\r\n     *\/\r\n    @Override\r\n    public synchronized int hashCode() {\r\n        final int prime = 31;\r\n        int result = 1;\r\n        result = (prime * result) + ((this.cache == null) ? 0 : this.cache.hashCode());\r\n        return result;\r\n    }\r\n\r\n    \/**\r\n     * Inserts an existing String cache into this instance.\r\n     *\r\n     * @param insert Cache to insert.\r\n     *\/\r\n    protected synchronized void insertCache(final StringCache insert) {\r\n        this.cache.putAll(insert.cache);\r\n    }\r\n\r\n    \/**\r\n     * Remove a String from the Cache.\r\n     *\r\n     * @param key String to remove.\r\n     *\/\r\n    public synchronized void remove(final String key) {\r\n        this.cache.remove(key);\r\n    }\r\n\r\n    \/**\r\n     * @return The size of the Cache.\r\n     * @see WeakHashMap#size()\r\n     *\/\r\n    public synchronized int size() {\r\n        return this.cache.size();\r\n    }\r\n\r\n    \/**\r\n     * Return a Properties object with all strings Cached.\r\n     * @param properties Properties to convert to Cached Strings\r\n     * @return new Properties object with cached strings.\r\n     *\/\r\n    public Properties cacheProperties(Properties properties) {\r\n        Properties cacheProperties = new Properties();\r\n        Set&lt;String> propertyNames = properties.stringPropertyNames();\r\n        propertyNames.forEach(propertyName -> cacheProperties.put(this.cache(propertyName),this.cache(properties.getProperty(propertyName))));\r\n\r\n        return cacheProperties;\r\n    }\r\n}\r\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes an application will have a large number of Strings. Due to memory we certainly do not want to keep multiple instances of duplicate strings in memory. A string cache can greatly reduce the number of Strings in memory. I had a case where I had millions of strings, and greatly needed to reduce the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3062,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Java String Cache - Optimize string caching for garbage collection","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[457],"tags":[69],"series":[],"class_list":["post-3060","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java_extra","tag-java-2"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/05\/geocache-398016_640.jpg?fit=640%2C480&ssl=1","jetpack-related-posts":[{"id":3225,"url":"https:\/\/www.mymiller.name\/wordpress\/java_extra\/caching-objects\/","url_meta":{"origin":3060,"position":0},"title":"Caching Objects","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Sometimes you need to maintain a large number of objects in memory. If it is strings you may want to take a look at StringCache, which is based on my Cache class presented here. Now the Cache workers simply call create an instance of Cache: Cache<Person> peopleCache = new Cache<>();\u2026","rel":"","context":"In &quot;Java Extras&quot;","block_context":{"text":"Java Extras","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_extra\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2022\/02\/geocache-gd1a671d59_640.jpg?fit=640%2C360&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2022\/02\/geocache-gd1a671d59_640.jpg?fit=640%2C360&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2022\/02\/geocache-gd1a671d59_640.jpg?fit=640%2C360&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3213,"url":"https:\/\/www.mymiller.name\/wordpress\/java_tips\/java-tips-part-4\/","url_meta":{"origin":3060,"position":1},"title":"Java Tips Part 4","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Continuing my Java Tips from experience that I have learned from code reviews to different programming tasks. Tip 16: Perform Bulk operations with Native Queries, or Stored Procedure It can be very tempting to do an update like the following: List<Person> people = ListUtils.safe(personRepository.findAll()).stream().filter person -> person.getAge() >= 60 ).map(person\u2026","rel":"","context":"In &quot;Java Tips&quot;","block_context":{"text":"Java Tips","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_tips\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3249,"url":"https:\/\/www.mymiller.name\/wordpress\/java_tips\/java-tips-part-5\/","url_meta":{"origin":3060,"position":2},"title":"Java Tips Part 5","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Tip 21: Use Prepared Statements When working with JPA\/Hibernate make use of Prepared Statements that can be reused. Basically, I'm saying do not do the following: Query query = JPA.em().createNativeQuery(\"select count(*) from user u inner join\" + \"address a where a.user_id=u.id and a.city='\" + city + \"'\"); BigInteger val =\u2026","rel":"","context":"In &quot;Java Tips&quot;","block_context":{"text":"Java Tips","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_tips\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":1936,"url":"https:\/\/www.mymiller.name\/wordpress\/java\/advancedstring-java-lang-string-steroids\/","url_meta":{"origin":3060,"position":3},"title":"AdvancedString &#8211; java.lang.String on steroids!","author":"Jeffery Miller","date":"November 24, 2025","format":false,"excerpt":"Every need an additional method on the String Class? \u00a0Well I have and it would have made life much easier. \u00a0Unfortunately you can't subclass String as it is Final. \u00a0So what are you to do? \u00a0Well you wrap the String class. \u00a0I have created AdvancedString, which contains additional functionality and\u2026","rel":"","context":"In &quot;JAVA&quot;","block_context":{"text":"JAVA","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java\/"},"img":{"alt_text":"AdvancedString","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/rope-1379561_640.jpg?fit=640%2C425&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/rope-1379561_640.jpg?fit=640%2C425&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/rope-1379561_640.jpg?fit=640%2C425&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3401,"url":"https:\/\/www.mymiller.name\/wordpress\/java\/ansi-colors\/","url_meta":{"origin":3060,"position":4},"title":"ANSI Colors","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Title: Using ANSI Colors in Java Code for String Styling Introduction: ANSI colors provide a powerful way to add visual enhancements and improve the readability of text in a terminal or console environment. In Java, you can leverage ANSI escape codes to apply various colors and formatting to your strings.\u2026","rel":"","context":"In &quot;JAVA&quot;","block_context":{"text":"JAVA","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2019\/04\/coding-924920_640.jpg?fit=640%2C426&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2019\/04\/coding-924920_640.jpg?fit=640%2C426&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2019\/04\/coding-924920_640.jpg?fit=640%2C426&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3548,"url":"https:\/\/www.mymiller.name\/wordpress\/spring\/beyond-the-basics-optimizing-your-spring-boot-applications-for-performance-fine-tune-your-application-for-speed-and-efficiency\/","url_meta":{"origin":3060,"position":5},"title":"Beyond the Basics: Optimizing Your Spring Boot Applications for Performance &#8211; Fine-tune your application for speed and efficiency.","author":"Jeffery Miller","date":"November 25, 2025","format":false,"excerpt":"Absolutely! Here\u2019s a blog article on optimizing Spring Boot applications, aimed at those who already have some experience with the framework: Beyond the Basics: Optimizing Your Spring Boot Applications for Performance Spring Boot is a fantastic framework for rapidly building production-ready applications. However, as your application grows and handles more\u2026","rel":"","context":"In &quot;Spring&quot;","block_context":{"text":"Spring","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/06\/ai-generated-8619544_1280.jpg?fit=1200%2C685&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/06\/ai-generated-8619544_1280.jpg?fit=1200%2C685&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/06\/ai-generated-8619544_1280.jpg?fit=1200%2C685&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/06\/ai-generated-8619544_1280.jpg?fit=1200%2C685&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/06\/ai-generated-8619544_1280.jpg?fit=1200%2C685&ssl=1&resize=1050%2C600 3x"},"classes":[]}],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/3060","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/comments?post=3060"}],"version-history":[{"count":1,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/3060\/revisions"}],"predecessor-version":[{"id":3061,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/3060\/revisions\/3061"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media\/3062"}],"wp:attachment":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media?parent=3060"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/categories?post=3060"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/tags?post=3060"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/series?post=3060"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}