{"id":2233,"date":"2025-12-18T10:00:01","date_gmt":"2025-12-18T15:00:01","guid":{"rendered":"http:\/\/www.mymiller.name\/wordpress\/?p=2233"},"modified":"2025-12-18T10:00:01","modified_gmt":"2025-12-18T15:00:01","slug":"javas-missing-treemap","status":"publish","type":"post","link":"https:\/\/www.mymiller.name\/wordpress\/java_extra\/javas-missing-treemap\/","title":{"rendered":"Java&#8217;s missing TreeMap"},"content":{"rendered":"<p>One thing I have always found lacking in Java collections\/containers is TreeMap. A simple class that takes a hierarchical approach to mapping data to a node.&nbsp; Given a hierarchical string like &#8220;\/java\/myapp\/javafx\/config\/phone&#8221; I can set the various objects relative to this on that node.<\/p>\n<p>Java Preferences() class supports this hierarchical approach but it is limited to native data types. I have never understood why this has been missing from the JRE.&nbsp; So I created my own implementation and here it is for anyone else looking.<\/p>\n<p>Here is the code to let you place any kind of object on a specified node.<\/p>\n\n\n<pre class=\"wp-block-code\"><code>package name.mymiller.extensions.containers;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\n\n\/**\n * @param &lt;T> Type of Object to map\n * @author jmiller\n * Class used to map values out on a tree.  Allows for infinite depth. Key names should be in form of\n * \"\/text\/text\/text\/....\"\n *\/\npublic class TreeMap&lt;T> {\n\n    \/**\n     * Roote Node of the tree also known as \"\/\"\n     *\/\n    private TreeNode root = null;\n\n    \/**\n     * Adds the Value to the node at specified Key\n     *\n     * @param key   Specified branch to add values to\n     * @param value Values to add at node\n     *\/\n    public void add(String key, T value) {\n        TreeNode node = this.findNode(key);\n\n        ArrayList&lt;T> values = node.getValues();\n        if (values == null) {\n            values = new ArrayList&lt;>();\n            node.setValues(values);\n        }\n\n        values.add(value);\n    }\n\n    \/**\n     * Add Collection of values to the specified node\n     *\n     * @param key        Specifies the node to add values to.\n     * @param collection Collection of values to add to the node.\n     * @return\n     *\/\n    public boolean addAll(String key, Collection&lt;? extends T> collection) {\n        TreeNode node = this.findNode(key);\n\n        ArrayList&lt;T> values = node.getValues();\n        if (values == null) {\n            values = new ArrayList&lt;T>();\n            node.setValues(values);\n        }\n\n        return values.addAll(collection);\n    }\n\n    \/**\n     * Remove the colleciton of values from the specified node\n     *\n     * @param key        Specifies the node to remove values from.\n     * @param collection Collection of values to remove.\n     * @return Boolean indicating if values remoted.\n     *\/\n    public boolean removeAll(String key, Collection&lt;?> collection) {\n        TreeNode node = this.findNode(key);\n\n        ArrayList&lt;T> values = node.getValues();\n        if (values == null) {\n            return false;\n        }\n\n        return values.removeAll(collection);\n    }\n\n    \/**\n     * Remove a value from specified node\n     *\n     * @param key   Specified node to remove value from\n     * @param value Valueto remove from specified node.\n     * @return boolean indicating success.\n     *\/\n    public boolean remove(String key, T value) {\n        TreeNode node = this.findNode(key);\n        ArrayList&lt;T> values = node.getValues();\n\n        if (values == null) {\n            return false;\n        }\n\n        return values.remove(value);\n    }\n\n    \/**\n     * Clear all values from specified node\n     *\n     * @param key Node to clear all values from.\n     *\/\n    public void clearNode(String key) {\n        TreeNode node = this.findNode(key);\n        ArrayList&lt;T> values = node.getValues();\n\n        if (values != null) {\n            values.clear();\n        }\n    }\n\n    \/**\n     * Returns the values associated with this key, or null if none are set.\n     *\n     * @param key Specified Node to return values on.\n     * @return List of values.\n     *\/\n    public List&lt;T> get(String key) {\n        TreeNode node = this.findNode(key);\n        return node.getValues();\n    }\n\n    \/**\n     * Internal method used to find\/create the tree structure to the specified node\n     *\n     * @param key Node to create\/find\n     * @return Node specified\n     *\/\n    private TreeNode findNode(String key) {\n        String&#91;] keys = key.split(\"\/\");\n\n        TreeNode currentNode = null;\n        for (String nodeKey : keys) {\n            if (nodeKey.isEmpty()) {\n                if (this.root == null) {\n                    this.root = new TreeNode();\n                }\n                currentNode = this.root;\n            } else if (currentNode == null) {\n                if (this.root == null) {\n                    this.root = new TreeNode();\n                }\n                currentNode = this.root;\n            }\n\n            if (!nodeKey.isEmpty()) {\n                HashMap&lt;String, TreeNode> children = currentNode.getChildren();\n                if (children == null) {\n                    currentNode.setChildren(new HashMap&lt;>());\n                    children = currentNode.getChildren();\n                } else {\n                    currentNode = children.get(nodeKey);\n                }\n\n                if (currentNode == null) {\n                    currentNode = new TreeNode();\n                    children.put(nodeKey, currentNode);\n                }\n            }\n        }\n        return currentNode;\n    }\n\n    \/**\n     * Internal class used to map out tree structure and hold values.\n     *\n     * @author jmiller\n     *\/\n    private class TreeNode {\n        \/**\n         * Values of this branch holds\n         *\/\n        private ArrayList&lt;T> values = null;\n\n        \/**\n         * Map directing to the children of this branch.\n         *\/\n        private HashMap&lt;String, TreeNode> children = null;\n\n        \/**\n         * Get the values of the current Node\n         *\n         * @return ArrayList of the values on this node\n         *\/\n        public ArrayList&lt;T> getValues() {\n            return values;\n        }\n\n        \/**\n         * Sets the values of this node.\n         *\n         * @param values ArrayList containing the values of this node.\n         *\/\n        public void setValues(ArrayList&lt;T> values) {\n            this.values = values;\n        }\n\n        \/**\n         * @return Get the HashMap that has the children of this node.\n         *\/\n        public HashMap&lt;String, TreeNode> getChildren() {\n            return children;\n        }\n\n        \/**\n         * Sets the HashMap for this node, with the children\n         *\n         * @param children Hashmap of the children of this node.\n         *\/\n        public void setChildren(HashMap&lt;String, TreeNode> children) {\n            this.children = children;\n        }\n    }\n\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>One thing I have always found lacking in Java collections\/containers is TreeMap. A simple class that takes a hierarchical approach to mapping data to a node.&nbsp; Given a hierarchical string like &#8220;\/java\/myapp\/javafx\/config\/phone&#8221; I can set the various objects relative to this on that node. Java Preferences() class supports this hierarchical approach but it is limited [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2238,"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":"","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":[248],"class_list":["post-2233","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java_extra","tag-java-2","series-advanced-classes"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2018\/04\/tree-3097419_640.jpg?fit=640%2C360&ssl=1","jetpack-related-posts":[{"id":3179,"url":"https:\/\/www.mymiller.name\/wordpress\/java_extra\/understanding-json-data-processing-with-java-exploring-the-jsonfieldprocessor-class\/","url_meta":{"origin":2233,"position":0},"title":"Understanding JSON Data Processing with Java: Exploring the JsonFieldProcessor Class","author":"Jeffery Miller","date":"January 15, 2026","format":false,"excerpt":"In today's digital era, data comes in various formats, with JSON (JavaScript Object Notation) being one of the most popular for representing structured data. Manipulating and processing JSON data efficiently is crucial for many software applications, from web development to data analysis. In this article, we'll delve into the workings\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\/2024\/04\/data-7798787_640.png?fit=640%2C640&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/04\/data-7798787_640.png?fit=640%2C640&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/04\/data-7798787_640.png?fit=640%2C640&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3795,"url":"https:\/\/www.mymiller.name\/wordpress\/spring_ai\/evrete-a-modern-java-rule-engine\/","url_meta":{"origin":2233,"position":1},"title":"EVRete: A Modern Java Rule Engine","author":"Jeffery Miller","date":"April 20, 2026","format":false,"excerpt":"EVRete is a high-performance, lightweight, and open-source rule engine designed for Java applications. It offers a flexible and expressive way to implement rule-based logic, making it a compelling alternative to traditional rule engines like Drools. This article delves into EVRete\u2019s features, focusing on its annotation-based approach to rule definition. Why\u2026","rel":"","context":"In &quot;Spring AI&quot;","block_context":{"text":"Spring AI","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring_ai\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/compliance-5899194_1280-jpg.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/compliance-5899194_1280-jpg.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/compliance-5899194_1280-jpg.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/compliance-5899194_1280-jpg.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/compliance-5899194_1280-jpg.avif 3x"},"classes":[]},{"id":3380,"url":"https:\/\/www.mymiller.name\/wordpress\/java\/synchronous-to-asynchronous\/","url_meta":{"origin":2233,"position":2},"title":"Synchronous to Asynchronous","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Converting a synchronous method to an asynchronous one in Java involves modifying the code to allow other tasks to execute while it is waiting for input\/output operations to complete. Here's an example of how to convert a synchronous method to an asynchronous one in Java: Let's say we have a\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\/2023\/06\/multitasking-ga6749a2b2_640.jpg?fit=640%2C394&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2023\/06\/multitasking-ga6749a2b2_640.jpg?fit=640%2C394&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2023\/06\/multitasking-ga6749a2b2_640.jpg?fit=640%2C394&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3903,"url":"https:\/\/www.mymiller.name\/wordpress\/docker\/the-s3-local-dev-trick-using-minio-to-simplify-cloud-native-developmen\/","url_meta":{"origin":2233,"position":3},"title":"The S3 Local Dev Trick: Using MinIO to Simplify Cloud-Native Developmen","author":"Jeffery Miller","date":"August 25, 2025","format":false,"excerpt":"As a software architect building cloud-native solutions, you know that working with cloud services like AWS S3 can be a bit tricky in a local development environment. You don't want to constantly connect to a remote bucket, and setting up complex local testing environments can be a pain. But what\u2026","rel":"","context":"In &quot;Docker&quot;","block_context":{"text":"Docker","link":"https:\/\/www.mymiller.name\/wordpress\/category\/docker\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-9268117_1280.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-9268117_1280.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-9268117_1280.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-9268117_1280.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/08\/ai-generated-9268117_1280.avif 3x"},"classes":[]},{"id":3786,"url":"https:\/\/www.mymiller.name\/wordpress\/spring_ai\/integrating-easy-rules-with-a-spring-boot-microservice\/","url_meta":{"origin":2233,"position":4},"title":"Integrating Easy Rules with a Spring Boot Microservice","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"This post will walk you through integrating the lightweight and straightforward Easy Rules engine with your Spring Boot microservice. We'll cover the necessary dependencies, basic setup, and an example service to demonstrate its usage. 1. Project Setup and Dependencies Start by creating a Spring Boot project. Next, add the following\u2026","rel":"","context":"In &quot;Spring AI&quot;","block_context":{"text":"Spring AI","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring_ai\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/office-4249395_1280-jpg.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/office-4249395_1280-jpg.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/office-4249395_1280-jpg.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/office-4249395_1280-jpg.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/office-4249395_1280-jpg.avif 3x"},"classes":[]},{"id":3704,"url":"https:\/\/www.mymiller.name\/wordpress\/spring_databases\/mastering-location-data-with-spring-jpa-a-comprehensive-guide\/","url_meta":{"origin":2233,"position":5},"title":"Mastering Location Data with Spring JPA: A Comprehensive Guide","author":"Jeffery Miller","date":"April 20, 2026","format":false,"excerpt":"In today\u2019s interconnected world, location data plays a pivotal role in numerous applications, from e-commerce and logistics to travel and social networking. Efficiently managing and accessing this wealth of geographical information is crucial for developers. This article delves into the realm of location data management using Spring JPA (Java Persistence\u2026","rel":"","context":"In &quot;Spring Databases&quot;","block_context":{"text":"Spring Databases","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring_databases\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/09\/international-1751293_1280.png?fit=1186%2C1200&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/09\/international-1751293_1280.png?fit=1186%2C1200&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/09\/international-1751293_1280.png?fit=1186%2C1200&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/09\/international-1751293_1280.png?fit=1186%2C1200&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/09\/international-1751293_1280.png?fit=1186%2C1200&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\/2233","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=2233"}],"version-history":[{"count":8,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/2233\/revisions"}],"predecessor-version":[{"id":2905,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/2233\/revisions\/2905"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media\/2238"}],"wp:attachment":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media?parent=2233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/categories?post=2233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/tags?post=2233"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/series?post=2233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}