{"id":13172,"date":"2017-09-15T12:17:56","date_gmt":"2017-09-15T10:17:56","guid":{"rendered":"https:\/\/touk.pl\/blog\/?p=13172"},"modified":"2017-09-21T12:41:30","modified_gmt":"2017-09-21T10:41:30","slug":"maintaining-priorityqueue-order-with-java-streams","status":"publish","type":"post","link":"https:\/\/touk.pl\/blog\/2017\/09\/15\/maintaining-priorityqueue-order-with-java-streams\/","title":{"rendered":"Maintaining PriorityQueue Order with Java Streams"},"content":{"rendered":"<p>The tricky thing about working with <em>PriorityQueues<\/em> is that, ironically, they don&#8217;t always behave according to the <em>PriorityQueue<\/em> semantics. <!--more--><\/p>\n<h2 id=\"priorityqueue-traversal\">PriorityQueue Traversal<\/h2>\n<p>If we have a look at <a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/api\/java\/util\/PriorityQueue.html#iterator()\">PriorityQueue.iterator()<\/a> documentation, we&#8217;ll see that, unintuitively, <em>iterator()<\/em> is not traversing the queue according to its priority order:<\/p>\n<blockquote><p>Returns an iterator over the elements in this queue. <strong>The iterator does not return the elements in any particular order.<\/strong><\/p><\/blockquote>\n<p>The same behaviour can be noticed when trying to use Java Stream API for processing <em>PriorityQueue&#8217;s<\/em> elements using the instance obtained by the <em>stream()<\/em> method &#8211; the <em>Stream<\/em> instance depends on the <em>Spliterator<\/em> instance which doesn&#8217;t guarantee the desired traversal order.<\/p>\n<pre>PriorityQueue&lt;String&gt; queue = new PriorityQueue&lt;&gt;(comparing(String::length));\nList&lt;String&gt; content = Arrays.asList(\"1\", \"333\", \"22\", \"55555\", \"4444\");\nqueue.addAll(content);\n\nassertThat(queue.stream())\n  .containsExactlyElementsOf(content);<\/pre>\n<p><strong>We can see that the insertion order gets preserved regardless of the fact that our queue was expected to be providing <em>Strings<\/em> according to their length.<\/strong><\/p>\n<h2 id=\"solution-1\">Solution #1<\/h2>\n<p>We can use the <em>poll()<\/em> method to fetch elements from the queue according to the priority order.<\/p>\n<p>So &#8211; let&#8217;s generate a <em>Stream<\/em> from consecutive elements, returned by the\u00a0<em>poll()<\/em>\u00a0method, using <em>Stream.generate()<\/em> method:<\/p>\n<pre>List&lt;String&gt; result = Stream.generate(queue::poll)\n  .limit(queue.size())\n  .collect(Collectors.toList());\n\nassertThat(result)\n  .containsExactly(\"1\", \"22\", \"333\", \"4444\", \"55555\");\nassertThat(queue)\n  .isEmpty();<\/pre>\n<p>The problem with this implementation is that <strong>it&#8217;s not concurrent-modification-friendly.<\/strong> Until Java 9 gets released, we can&#8217;t terminate the generated <em>Stream<\/em> dynamically so we need to rely on limiting the <em>Stream<\/em> size to the queue size &#8211; which is not perfect because this can change during an actual processing.<\/p>\n<p><strong>The crucial part of this implementation is that after consuming a <em>Stream<\/em> instance, we end up with a modified queue<\/strong> &#8211; the <em>poll()<\/em> method removes the polled element from the queue.<\/p>\n<p>Eventually, this approach can be extracted to the separate utility method:<\/p>\n<pre>static &lt;T&gt; Stream&lt;T&gt; drainToStream(PriorityQueue&lt;T&gt; queue) {\n    Objects.requireNonNull(queue);\n    return Stream.generate(queue::poll)\n      .limit(queue.size());\n}<\/pre>\n<h3 id=\"java-9\">Java 9<\/h3>\n<p>Since Java 9, it&#8217;ll be possible to rewrite it in a concurrent-friendly manner:<\/p>\n<pre>static &lt;T&gt; Stream&lt;T&gt; drainToStream(PriorityQueue&lt;T&gt; queue) {\n    Objects.requireNonNull(queue);\n    return Stream.generate(queue::poll)\n      .takeWhile(Objects::nonNull)\n}<\/pre>\n<h2 id=\"solution-2\">Solution #2<\/h2>\n<p>The second approach involves simply sorting the <em>Stream<\/em> instance using the same comparator that the queue uses. We need to remember that this will work as long as the queue was initialized using a custom comparator:<\/p>\n<pre>List&lt;String&gt; result = queue.stream()\n  .sorted(queue.comparator())\n  .collect(Collectors.toList());\n\nassertThat(result)\n  .containsExactly(\"1\", \"22\", \"333\", \"4444\", \"55555\");\nassertThat(queue)\n  .isNotEmpty();<\/pre>\n<p>This approach can be used with any <em>Collection<\/em> type (as long as we can get ahold of the right\u00a0<em>Comparator<\/em> instance).<\/p>\n<p>If we store <em>Comparable<\/em> objects in the queue and depend on their natural order, this becomes even simpler because we do not need to reach for the <em>Comparator<\/em> instance:<\/p>\n<pre>PriorityQueue&lt;String&gt; queue = new PriorityQueue&lt;&gt;();\nqueue.addAll(Arrays.asList(\"1\", \"333\", \"22\", \"55555\", \"4444\"));\n\nList&lt;String&gt; result = queue.stream()\n  .sorted()\n  .collect(Collectors.toList());\n\nassertThat(result)\n  .containsExactly(\"1\", \"22\", \"333\", \"4444\", \"55555\");\nassertThat(queue)\n  .isNotEmpty();<\/pre>\n<p><strong>In this case, after consuming a <em>Stream<\/em> instance, our original queue remains intact.<\/strong><\/p>\n<p>Eventually, this approach can be extracted to the separate utility method:<\/p>\n<pre>static &lt;T&gt; Stream&lt;T&gt; asStream(PriorityQueue&lt;T&gt; queue) {\n    Objects.requireNonNull(queue);\n    Comparator&lt;? super T&gt; comparator = queue.comparator();\n    return comparator != null\n      ? queue.stream().sorted(comparator)\n      : queue.stream().sorted();\n}<\/pre>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>The priority order of the <em>PriorityQueue<\/em> is not preserved when iterating\/traversing so, essentially, we need to create our <em>Stream<\/em> instances ourselves.<\/p>\n<p>The working code snippets <a href=\"https:\/\/github.com\/pivovarit\/articles\/blob\/master\/priorityqueue-stream-order\/src\/test\/java\/com\/pivovarit\/priority\/PriorityQueueToStreamTest.java\">can be found on GitHub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"The tricky thing about working with PriorityQueues is that, ironically, they don&#8217;t always behave according to the PriorityQueue&hellip;\n","protected":false},"author":68,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":{"0":"post-13172","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-development-design"},"_links":{"self":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13172","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/users\/68"}],"replies":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/comments?post=13172"}],"version-history":[{"count":6,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13172\/revisions"}],"predecessor-version":[{"id":13220,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13172\/revisions\/13220"}],"wp:attachment":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/media?parent=13172"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/categories?post=13172"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/tags?post=13172"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}