{"id":130,"date":"2024-04-25T18:56:15","date_gmt":"2024-04-25T17:56:15","guid":{"rendered":"https:\/\/dharma-code.com\/wordpress\/?p=130"},"modified":"2024-04-26T16:32:11","modified_gmt":"2024-04-26T15:32:11","slug":"enummap-performance","status":"publish","type":"post","link":"https:\/\/dharma-code.com\/wordpress\/index.php\/2024\/04\/25\/enummap-performance\/","title":{"rendered":"EnumMap Performance Analysis (Java)"},"content":{"rendered":"\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button has-custom-width wp-block-button__width-50 has-custom-font-size is-style-outline mb-3 has-medium-font-size is-style-outline--1\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/github.com\/dharma-code-chris\/enum-collections-performance\" style=\"border-radius:6px\" target=\"_blank\" rel=\"noreferrer noopener\">View on GitHub<\/a><\/div>\n<\/div>\n\n\n\n<p>There is a special type of <code>java.util.Map<\/code> that we can use when the keys are enums: <code>java.util.EnumMap<\/code>.<\/p>\n\n\n\n<p>It is my experience that developers are often unaware of the <code>EnumMap<\/code> and will tend instead to use <code>HashMap<\/code> or <code>LinkedHashMap<\/code> even when they are using enums as keys.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Internal mechanisms<\/h2>\n\n\n\n<p><code>HashMap<\/code> is represented internally as a dynamically resized table of hash bin nodes. The keys must be hashed, and the <code>get<\/code> and <code>put<\/code> algorithms have a reasonable degree of complexity.<\/p>\n\n\n\n<p><code>LinkedHashMap<\/code> extends <code>HashMap<\/code>. <code>LinkedHashMap<\/code> uses a hash table combined with a doubly-linked list to give a predictable iteration order. For this reason they have even more complexity than basic hash maps.<\/p>\n\n\n\n<p><code>EnumMap<\/code> is based on the following fact: If we know the entire set of all possible keys of a Map in advance, then we can use a more efficient algorithm than if we need to support any arbitrary set of keys. Enum maps are represented internally as arrays, which allows for an efficient implementation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Performance comparison<\/h2>\n\n\n\n<p>I decided to investigate the performance difference between the <code>EnumMap<\/code> and these two kinds of hash map. The results clearly show that the <code>EnumMap<\/code> has a significant performance advantage over hash maps. These performance advantages become more apparent the more values there are in the enum, and the more read-cycles we perform on the map instance.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test approach<\/h2>\n\n\n\n<p>For this test I am using Java 17.<\/p>\n\n\n\n<p>First, I defined four different enums, with the following number of values respectively: 3, 5, 10 and 20. These sizes I felt would give a broad sample of the typical size of enums.<\/p>\n\n\n\n<p>I then wrote the following generic test function to test any type of enum with any type of map:<\/p>\n\n\n\n<pre class=\"wp-block-code language-java\"><code>private static final int ITERATIONS = 1_000_000;\nprivate static final int READ_CYCLES = 10;\n\n\/\/ ...\n\nprivate &lt;M extends Map&lt;E, Integer&gt;, E extends Enum&lt;E&gt;&gt; void testMapPerformance(\n\t\tE&#91;] values, Supplier&lt;M&gt; mapSupplier, String mapName) {\n\t\n\tlong total = 0;\n\n\tInstant start = Instant.now();\n\n\tfor (int i = 0; i &lt; ITERATIONS; i++) {\n\t\tMap&lt;E, Integer&gt; map = mapSupplier.get();\n\t\tfor (int j = 0; j &lt; values.length; j++) {\n\t\t\tmap.put(values&#91;j], j);\n\t\t}\n\t\tfor (int r = 0; r &lt; READ_CYCLES; r++) {\n\t\t\tfor (E value : values) {\n\t\t\t\ttotal += map.get(value);\n\t\t\t}\n\t\t}\n\t}\n\n\tInstant end = Instant.now();\n\n\t\/\/ ...\n}<\/code><\/pre>\n\n\n\n<p>The function times how long it takes to perform one million iterations of:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>create a new map;<\/li>\n\n\n\n<li>fill it with with each enum value as the map entry key and the value&#8217;s index as the map entry value; and<\/li>\n\n\n\n<li>read each value from the map a set number of times as defined by the <code>READ_CYCLES<\/code> constant.<\/li>\n<\/ol>\n\n\n\n<p>I then wrote a dozen test methods, four for each of the different map types <code>EnumMap<\/code>, <code>HashMap<\/code> and <code>LinkedHashMap<\/code>. Each of the maps&#8217; four test methods tests the performance using the different enum sizes of 3, 5, 10 and 20 respectively.<\/p>\n\n\n\n<p>Adding a <code>StringBuilder<\/code> with CSV generating formatting then made it easy to tabulate the results of each test execution. I created a spreadsheet for the analysis and then executed 10 test runs for each of the following values of <code>READ_CYCLES<\/code>: 1, 3, 5, 10 and 20. There were therefore 50 test runs in total.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Results<\/h2>\n\n\n\n<p>Here are the results of the analysis, with times given in milliseconds:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><img loading=\"lazy\" decoding=\"async\" width=\"727\" height=\"440\" src=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-1.png\" alt=\"\" class=\"wp-image-163\" srcset=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-1.png 727w, https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-1-300x182.png 300w\" sizes=\"auto, (max-width: 727px) 100vw, 727px\" \/><figcaption class=\"wp-element-caption\">Results for <code>READ_CYCLES<\/code> == 1<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"740\" height=\"432\" src=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-3.png\" alt=\"\" class=\"wp-image-164\" srcset=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-3.png 740w, https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-3-300x175.png 300w\" sizes=\"auto, (max-width: 740px) 100vw, 740px\" \/><figcaption class=\"wp-element-caption\">Results for <code>READ_CYCLES<\/code> == 3<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"741\" height=\"446\" src=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-5.png\" alt=\"\" class=\"wp-image-165\" srcset=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-5.png 741w, https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-5-300x181.png 300w\" sizes=\"auto, (max-width: 741px) 100vw, 741px\" \/><figcaption class=\"wp-element-caption\">Results for <code>READ_CYCLES<\/code> == 5<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"747\" height=\"441\" src=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-10.png\" alt=\"\" class=\"wp-image-166\" srcset=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-10.png 747w, https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-10-300x177.png 300w\" sizes=\"auto, (max-width: 747px) 100vw, 747px\" \/><figcaption class=\"wp-element-caption\">Results for <code>READ_CYCLES<\/code> == 10<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"748\" height=\"450\" src=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-20.png\" alt=\"\" class=\"wp-image-167\" srcset=\"https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-20.png 748w, https:\/\/dharma-code.com\/wordpress\/wp-content\/uploads\/2024\/04\/read-cycles-20-300x180.png 300w\" sizes=\"auto, (max-width: 748px) 100vw, 748px\" \/><figcaption class=\"wp-element-caption\">Results for <code>READ_CYCLES<\/code> == 20<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Discussion<\/h2>\n\n\n\n<p>In nearly every case, <code>EnumMap<\/code> shows a performance improvement when compared to the two hash maps. Similarly, in most cases, <code>HashMap<\/code> shows a performance improvement when compared to <code>LinkedHashMap<\/code>.<\/p>\n\n\n\n<p>You may have noticed that when the read cycles are low and the number of enum values is also low, the performance difference is less noticeable. Indeed, if read cycles is 1 and enum value count is 3 then <code>HashMap<\/code> has a marginal performance advantage when compared to <code>EnumMap<\/code>! This is because there is a slightly higher overhead when it comes to creating an <code>EnumMap<\/code> relative to creating a <code>HashMap<\/code>, and if sufficient use is not made of the read efficiencies of the <code>EnumMap<\/code>, the creation overhead predominates.<\/p>\n\n\n\n<p>However, in most cases it would still be advisable to use <code>EnumMap<\/code> whenever possible, since this is an edge case and the performance difference is minimal. The knowledge shared by using <code>EnumMap<\/code> whenever possible is arguably worth a tiny performance drop in such situations.<\/p>\n\n\n\n<p>I noticed that for 20 read cycles, there appears to be an anomalous result for enums with 5 values for <code>HashMap<\/code> when compared to <code>LinkedHashMap<\/code>, recording averages of 1118.4ms and 908.2ms respectively. I  do not intend to investigate this as it is outside the scope of this article, but it is worth noting as it may be of interest to someone if it is a real effect and not simply a random chance outlier.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>In conclusion, when you want to use a <code>Map<\/code> with enums as keys, you should probably use an <code>EnumMap<\/code>!<\/p>\n\n\n\n<p>However, if iteration order is important to you, and for some reason you wanted to iterate in a different order from the order in which the enum values are declared (otherwise known as their &#8216;ordinal&#8217;) then you should use a <code>LinkedHashMap<\/code>. In such a case you might also consider whether you could simply re-order the values in the enum&#8217;s definition to make use of the <code>EnumMap<\/code> here too.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Performance analysis of Java&#8217;s EnumMap vs HashMap vs LinkedHashMap<\/p>\n","protected":false},"author":1,"featured_media":149,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[2,11],"tags":[],"class_list":["post-130","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","category-performance"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/130","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/comments?post=130"}],"version-history":[{"count":21,"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/130\/revisions"}],"predecessor-version":[{"id":182,"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/130\/revisions\/182"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/media\/149"}],"wp:attachment":[{"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=130"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=130"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dharma-code.com\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=130"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}