<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"Viktor Klang" <viktor.klang@oracle.com><br><b>To: </b>"Remi Forax" <forax@univ-mlv.fr><br><b>Cc: </b>"core-libs-dev" <core-libs-dev@openjdk.java.net>, "Paul Sandoz" <psandoz@openjdk.java.net><br><b>Sent: </b>Thursday, January 18, 2024 5:14:38 PM<br><b>Subject: </b>Re: Gatherer: spliterator characteristics are not propagated<br></blockquote></div><div><style style="display:none;"> P {margin-top:0;margin-bottom:0;} </style></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><span style="color: #000000;" data-mce-style="color: #000000;"><br></span></div><blockquote style="margin-right: 0px; margin-left: 0.8ex; padding-left: 1ex; border-left: 3px solid #c8c8c8;" data-mce-style="margin-right: 0px; margin-left: 0.8ex; padding-left: 1ex; border-left: 3px solid #c8c8c8;"><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">And for A.andThen(B), A.flags & B.flags should work, the stream is sorted if both gatherers keep it sorted.</div></blockquote><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">That is unfortunately not the case. That would presume that you can implement the composition such that it can preserve all the common flags. Some flags could be "dominant" and some "recessive" to use genetics nomenclature.</div></blockquote><div><br></div><div>Some flags of the stream pipeline are "recessive", mainly SHORT_CIRCUIT, but not the characteristics of the Gatherer which can have the corresponding "dominant" flag, GREEDY, in this case.</div><div>And the same for sequential, the flag should be PARALELIZABLE and not SEQUENTIAL.</div><div><br></div><div>The idea is that the Gatherer characteristics can have the same bit set at the same position as the stream op flags (as defined by StreamOpFlag).<br></div><div>So KEEP_DISTINCT is in position 0, KEEP_SORTED in in position 2 and KEEP_SIZED is in position 3.</div><div>For GREEDY, we use the same position as SHORT_CIRCUIT and we will flip the bit (using XOR) when we want to extract the stream op flags from the characteristics</div><div><br></div><div>All the factory methods call the generic of() with a combination of PARALELIZABLE and STATELESS and the user can adds the characteristics GREEDY, KEEP_DISTINCT, KEEP_SORTED and KEEP_SIZED (otherwise an exception should be raised).<br></div><div><br></div><div>In StreamOpFlag, there are two unused positions (14 and 15), that's perfect for our two new states PARALELIZABLE and STATELESS, so no problem here (technically we can also reuse positions of the Spliterator characteristic given that those flags are masked before being sent to the GathererOp super constructor).</div><div><br></div><div>The way to transform a Gatherer characteristics op to a stream flags op is to flip the bits corresponding to SHORT_CIRCUIT, add the highter bit of all flags but SHORT-CIRCUIT (because stream op flags are encoded using 2 bits) and mask to only retain SHORT_CIRCUIT, KEEP_DISTINCT, KEEP_SORTED and KEEP_SIZED.<br></div><div><br></div><div><div style="background-color: #ffffff; color: #080808;" data-mce-style="background-color: #ffffff; color: #080808;"><pre style="font-family: 'JetBrains Mono',monospace; font-size: 9.8pt;" data-mce-style="font-family: 'JetBrains Mono',monospace; font-size: 9.8pt;"><span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static int </span><span style="color: #00627a;" data-mce-style="color: #00627a;">toOpFlags</span>(<span style="color: #0033b3;" data-mce-style="color: #0033b3;">int </span><span style="color: #000000;" data-mce-style="color: #000000;">characteristics</span>) {<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">return  </span>((<span style="color: #000000;" data-mce-style="color: #000000;">characteristics </span>^ <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SHORT_CIRCUIT_MASK</span>) | <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">HIGHER_BITS</span>) & <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STREAM_OP_MASK</span>;<br>}</pre></div></div><div><br></div><div>see below for a full script.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><blockquote style="margin-right: 0px; margin-left: 0.8ex; padding-left: 1ex; border-left: 3px solid #c8c8c8;" data-mce-style="margin-right: 0px; margin-left: 0.8ex; padding-left: 1ex; border-left: 3px solid #c8c8c8;"><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">I suppose that if those flags already exist, it's because they have a purpose and i do not understand how it can make the other operations slower.</div></blockquote><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Extra invocations, extra storage, and extra composition overhead is not free. Since Stream is one-shot you need to include the construction cost with the execution cost. For something like an empty Stream construction cost scan be 90+% of the total costs.</div></blockquote><div><br></div><div>If you create a Gatherer, the characteristics is a constant (so the validity check is removed, it's just a mask and a test) so the result of calling toOpFlags() is a constant too.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>If the factory method is not inlined, the cost is 3 bitwise operations which is I believe faster than the "instanceof Greedy" used in the current code.</div><div><br data-mce-bogus="1"></div><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div id="Signature"><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Cheers,<br>√</div></div></blockquote><div><br></div><div>regards,<br></div><div>Rémi<br data-mce-bogus="1"></div><div><br></div><div>---</div><div><div style="background-color: #ffffff; color: #080808;" data-mce-style="background-color: #ffffff; color: #080808;"><pre style="font-family: 'JetBrains Mono',monospace; font-size: 9.8pt;" data-mce-style="font-family: 'JetBrains Mono',monospace; font-size: 9.8pt;"><span style="color: #0033b3;" data-mce-style="color: #0033b3;">public class </span><span style="color: #000000;" data-mce-style="color: #000000;">GathererFlag </span>{<br>  <span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">// cut and paste from StreamOpFlag<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">  /**<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">   * The bit pattern for setting/injecting a flag.<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">   */<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">  </span><span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span>= <span style="color: #1750eb;" data-mce-style="color: #1750eb;">0b01</span>;<br><br>  <span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">/**<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">   * The bit pattern for clearing a flag.<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">   */<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">  </span><span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">CLEAR_BITS </span>= <span style="color: #1750eb;" data-mce-style="color: #1750eb;">0b10</span>;<br><br>  <span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">/**<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">   * The bit pattern for preserving a flag.<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">   */<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">  </span><span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PRESERVE_BITS </span>= <span style="color: #1750eb;" data-mce-style="color: #1750eb;">0b11</span>;<br><br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static int </span><span style="color: #00627a;" data-mce-style="color: #00627a;">position</span>(<span style="color: #0033b3;" data-mce-style="color: #0033b3;">int </span><span style="color: #000000;" data-mce-style="color: #000000;">opFlagSet</span>) {<br>    <span style="color: #0033b3;" data-mce-style="color: #0033b3;">return </span><span style="color: #000000;" data-mce-style="color: #000000;">Integer</span>.<span style="font-style: italic;" data-mce-style="font-style: italic;">numberOfTrailingZeros</span>(<span style="color: #000000;" data-mce-style="color: #000000;">opFlagSet</span>) >> <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>;<br>  }<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">DISTINCT_POSITION </span>= <span style="font-style: italic;" data-mce-style="font-style: italic;">position</span>(<span style="color: #000000;" data-mce-style="color: #000000;">StreamOpFlag</span>.<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">IS_DISTINCT</span>);<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SORTED_POSITION </span>= <span style="font-style: italic;" data-mce-style="font-style: italic;">position</span>(<span style="color: #000000;" data-mce-style="color: #000000;">StreamOpFlag</span>.<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">IS_SORTED</span>);<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SIZED_POSITION </span>= <span style="font-style: italic;" data-mce-style="font-style: italic;">position</span>(<span style="color: #000000;" data-mce-style="color: #000000;">StreamOpFlag</span>.<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">IS_SIZED</span>);<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SHORT_CIRCUIT_POSITION </span>= <span style="font-style: italic;" data-mce-style="font-style: italic;">position</span>(<span style="color: #000000;" data-mce-style="color: #000000;">StreamOpFlag</span>.<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">IS_SHORT_CIRCUIT</span>);<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS_POSITION </span>= <span style="color: #1750eb;" data-mce-style="color: #1750eb;">14</span>;<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PARELLIZABLE_POSITION </span>= <span style="color: #1750eb;" data-mce-style="color: #1750eb;">15</span>;<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PARELLIZABLE </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span><< (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PARELLIZABLE_POSITION </span><< <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>);<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span><< (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS_POSITION </span><< <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>);<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">GREEDY </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span><< (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SHORT_CIRCUIT_POSITION </span><< <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>);<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span><< (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">DISTINCT_POSITION </span><< <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>);<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SORTED </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span><< (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SORTED_POSITION </span><< <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>);<br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span><< (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SIZED_POSITION </span><< <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>);<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SHORT_CIRCUIT_MASK </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SET_BITS </span><< (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SHORT_CIRCUIT_POSITION </span><< <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>);<br><br>  <span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">// no GREEDY here<br></span><span style="color: #8c8c8c; font-style: italic;" data-mce-style="color: #8c8c8c; font-style: italic;">  </span><span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">HIGHER_BITS </span>= (<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PARELLIZABLE </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SORTED </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED</span>) << <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>;<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">private static final int </span><span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STREAM_OP_MASK </span>=<br>      ((<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">GREEDY </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SORTED </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED</span>) << <span style="color: #1750eb;" data-mce-style="color: #1750eb;">1</span>) |<br>      <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">GREEDY </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SORTED </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED</span>;<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static </span><span style="color: #000000;" data-mce-style="color: #000000;">String </span><span style="color: #00627a;" data-mce-style="color: #00627a;">toString</span>(<span style="color: #0033b3;" data-mce-style="color: #0033b3;">int </span><span style="color: #000000;" data-mce-style="color: #000000;">characteristics</span>) {<br>    <span style="color: #0033b3;" data-mce-style="color: #0033b3;">return </span><span style="color: #000000;" data-mce-style="color: #000000;">Stream</span>.<span style="font-style: italic;" data-mce-style="font-style: italic;">of</span>(<span style="color: #000000;" data-mce-style="color: #000000;">characteristics</span>)<br>        .<<span style="color: #000000;" data-mce-style="color: #000000;">String</span>>mapMulti((<span style="color: #000000;" data-mce-style="color: #000000;">f</span>, <span style="color: #000000;" data-mce-style="color: #000000;">consumer</span>) -> {<br>          <span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>((<span style="color: #000000;" data-mce-style="color: #000000;">f </span>& <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PARELLIZABLE</span>) == <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PARELLIZABLE</span>) {<br>            <span style="color: #000000;" data-mce-style="color: #000000;">consumer</span>.accept(<span style="color: #067d17;" data-mce-style="color: #067d17;">"PARELLIZABLE"</span>);<br>          }<br>          <span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>((<span style="color: #000000;" data-mce-style="color: #000000;">f </span>& <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS</span>) == <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS</span>) {<br>            <span style="color: #000000;" data-mce-style="color: #000000;">consumer</span>.accept(<span style="color: #067d17;" data-mce-style="color: #067d17;">"STATELESS"</span>);<br>          }<br>          <span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>((<span style="color: #000000;" data-mce-style="color: #000000;">f </span>& <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">GREEDY</span>) == <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">GREEDY</span>) {<br>            <span style="color: #000000;" data-mce-style="color: #000000;">consumer</span>.accept(<span style="color: #067d17;" data-mce-style="color: #067d17;">"GREEDY"</span>);<br>          }<br>          <span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>((<span style="color: #000000;" data-mce-style="color: #000000;">f </span>& <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT</span>) == <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT</span>) {<br>            <span style="color: #000000;" data-mce-style="color: #000000;">consumer</span>.accept(<span style="color: #067d17;" data-mce-style="color: #067d17;">"KEEP_DISTINCT"</span>);<br>          }<br>          <span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>((<span style="color: #000000;" data-mce-style="color: #000000;">f </span>& <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SORTED</span>) == <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SORTED</span>) {<br>            <span style="color: #000000;" data-mce-style="color: #000000;">consumer</span>.accept(<span style="color: #067d17;" data-mce-style="color: #067d17;">"KEEP_SORTED"</span>);<br>          }<br>          <span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>((<span style="color: #000000;" data-mce-style="color: #000000;">f </span>& <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED</span>) == <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED</span>) {<br>            <span style="color: #000000;" data-mce-style="color: #000000;">consumer</span>.accept(<span style="color: #067d17;" data-mce-style="color: #067d17;">"KEEP_SIZED"</span>);<br>          }<br>        })<br>        .collect(<span style="color: #000000;" data-mce-style="color: #000000;">Collectors</span>.<span style="font-style: italic;" data-mce-style="font-style: italic;">joining</span>(<span style="color: #067d17;" data-mce-style="color: #067d17;">", "</span>));<br>  }<br><br>  <span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static int </span><span style="color: #00627a;" data-mce-style="color: #00627a;">toOpFlags</span>(<span style="color: #0033b3;" data-mce-style="color: #0033b3;">int </span><span style="color: #000000;" data-mce-style="color: #000000;">characteristics</span>) {<br>    <span style="color: #0033b3;" data-mce-style="color: #0033b3;">return  </span>((<span style="color: #000000;" data-mce-style="color: #000000;">characteristics </span>^ <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">SHORT_CIRCUIT_MASK</span>) | <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">HIGHER_BITS</span>) & <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STREAM_OP_MASK</span>;<br>}<br><br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">public static </span><span style="color: #000000;" data-mce-style="color: #000000;">String </span><span style="color: #00627a;" data-mce-style="color: #00627a;">toOpFlagsString</span>(<span style="color: #0033b3;" data-mce-style="color: #0033b3;">int </span><span style="color: #000000;" data-mce-style="color: #000000;">opFlags</span>) {<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">return </span><span style="color: #000000;" data-mce-style="color: #000000;">Arrays</span>.<span style="font-style: italic;" data-mce-style="font-style: italic;">stream</span>(<span style="color: #000000;" data-mce-style="color: #000000;">StreamOpFlag</span>.<span style="font-style: italic;" data-mce-style="font-style: italic;">values</span>())<br>.map(<span style="color: #000000;" data-mce-style="color: #000000;">op </span>-> {<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>(<span style="color: #000000;" data-mce-style="color: #000000;">op</span>.isPreserved(<span style="color: #851691;" data-mce-style="color: #851691;">opFlags</span>)) {<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">return </span><span style="color: #067d17;" data-mce-style="color: #067d17;">"preserved " </span>+ <span style="color: #000000;" data-mce-style="color: #000000;">op</span>;<br>}<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>(<span style="color: #000000;" data-mce-style="color: #000000;">op</span>.isCleared(<span style="color: #851691;" data-mce-style="color: #851691;">opFlags</span>)) {<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">return </span><span style="color: #067d17;" data-mce-style="color: #067d17;">"cleared " </span>+ <span style="color: #000000;" data-mce-style="color: #000000;">op</span>;<br>}<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">if </span>(<span style="color: #000000;" data-mce-style="color: #000000;">op</span>.isKnown(<span style="color: #851691;" data-mce-style="color: #851691;">opFlags</span>)) {<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">return </span><span style="color: #067d17;" data-mce-style="color: #067d17;">"set " </span>+ <span style="color: #000000;" data-mce-style="color: #000000;">op</span>;<br>}<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">return </span><span style="color: #067d17;" data-mce-style="color: #067d17;">"?? " </span>+ <span style="color: #000000;" data-mce-style="color: #000000;">op</span>;<br>})<br>.collect(<span style="color: #000000;" data-mce-style="color: #000000;">Collectors</span>.<span style="font-style: italic;" data-mce-style="font-style: italic;">joining</span>(<span style="color: #067d17;" data-mce-style="color: #067d17;">", "</span>));<br>}<br><br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">void </span><span style="color: #00627a;" data-mce-style="color: #00627a;">main</span>() {<br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">var </span><span style="color: #000000;" data-mce-style="color: #000000;">characteristics </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">PARELLIZABLE </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">GREEDY </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SORTED </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED</span>;<br><span style="color: #000000;" data-mce-style="color: #000000;">System</span>.<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">out</span>.println(<span style="font-style: italic;" data-mce-style="font-style: italic;">toOpFlagsString</span>(<span style="font-style: italic;" data-mce-style="font-style: italic;">toOpFlags</span>(<span style="color: #000000;" data-mce-style="color: #000000;">characteristics</span>)));<br><br><span style="color: #0033b3;" data-mce-style="color: #0033b3;">var </span><span style="color: #000000;" data-mce-style="color: #000000;">characteristics2 </span>= <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">STATELESS </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_DISTINCT </span>| <span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">KEEP_SIZED</span>;<br><span style="color: #000000;" data-mce-style="color: #000000;">System</span>.<span style="color: #871094; font-style: italic;" data-mce-style="color: #871094; font-style: italic;">out</span>.println(<span style="font-style: italic;" data-mce-style="font-style: italic;">toOpFlagsString</span>(<span style="font-style: italic;" data-mce-style="font-style: italic;">toOpFlags</span>(<span style="color: #000000;" data-mce-style="color: #000000;">characteristics2</span>)));<br>}<br>}</pre></div></div><div><br></div><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div id="Signature"><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><b><br></b></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><b>Viktor Klang</b></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Software Architect, Java Platform Group<br>Oracle</div></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><hr style="display: inline-block; width: 98%;" data-mce-style="display: inline-block; width: 98%;"><div id="divRplyFwdMsg" dir="ltr"><span style="font-family: Calibri, sans-serif; font-size: 11pt; color: #000000;" data-mce-style="font-family: Calibri, sans-serif; font-size: 11pt; color: #000000;"><b>From:</b> forax@univ-mlv.fr <forax@univ-mlv.fr><br><b>Sent:</b> Thursday, 18 January 2024 16:17<br><b>To:</b> Viktor Klang <viktor.klang@oracle.com><br><b>Cc:</b> core-libs-dev <core-libs-dev@openjdk.java.net>; Paul Sandoz <psandoz@openjdk.java.net><br><b>Subject:</b> [External] : Re: Gatherer: spliterator characteristics are not propagated</span><div> </div></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><hr><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><span style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;"><b>From: </b>"Viktor Klang" <viktor.klang@oracle.com><br><b>To: </b>"Remi Forax" <forax@univ-mlv.fr><br><b>Cc: </b>"core-libs-dev" <core-libs-dev@openjdk.java.net>, "Paul Sandoz" <psandoz@openjdk.java.net><br><b>Sent: </b>Thursday, January 18, 2024 3:36:07 PM<br><b>Subject: </b>Re: Gatherer: spliterator characteristics are not propagated<br></span></blockquote><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">I suspect that it is a rather slippery slope, once KEEP-flags are added, then others will want to be able to have INJECT-flags, and then people might have different opinions w.r.t. the default should be to clear all flags etc.</div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">And that's even before one looks at the composition-part of it, what are the flags for A.andThen(B)? (then extrapolate to N compositions and the available set of flags always approaches 0)</div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">I spent quite a bit of time on this and in the end tracking all this info, and making sure that the flags of implementations correspond to the actual behavior, just ended up costing performance for most streams and introduced an extra dimension to creation and maintenance which I had a hard time justifying.</div></blockquote><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">It can be a slippery slope if we were designing from the ground up but the stream implementation already exists and SORTED, DISTINCT and SIZED are the flags that are already tracked by the current implementation.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">Currently only SHORT_CIRCUIT is set (if not greedy),</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">see <a href="https://urldefense.com/v3/__https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/stream/GathererOp.java*L209__;Iw!!ACWV5N9M2RV99hQ!PhMxqlDzLWPRuwYc7ECRKNPVs0BtnoE-RdT-Jdkng7S-iFuERAHYcWvJ-OMKGLrkPdSrUl3xj1R9ypyeqeWI$" id="OWA1a26d136-987c-a5b2-e72e-dde19d919aa9" class="OWAAutoLink" target="_blank" rel="noopener" data-mce-href="https://urldefense.com/v3/__https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/stream/GathererOp.java*L209__;Iw!!ACWV5N9M2RV99hQ!PhMxqlDzLWPRuwYc7ECRKNPVs0BtnoE-RdT-Jdkng7S-iFuERAHYcWvJ-OMKGLrkPdSrUl3xj1R9ypyeqeWI$"> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/stream/GathererOp.java#L209</a></span><br data-mce-bogus="1"></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">And for A.andThen(B), A.flags & B.flags should work, the stream is sorted if both gatherers keep it sorted.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Making specific, rare, combinations of operations faster at the expense of making 99% of all others slower is a hard pill for most to swallow.</div></blockquote><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">I suppose that if those flags already exist, it's because they have a purpose and i do not understand how it can make the other operations slower.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div id="x_Signature"><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Cheers,<br>√</div></div></blockquote><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">regards,</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">Rémi</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><div id="x_Signature"><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><b><br></b></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><b>Viktor Klang</b></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Software Architect, Java Platform Group<br>Oracle</div></div><hr style="display: inline-block; width: 98%;" data-mce-style="display: inline-block; width: 98%;"><div id="x_divRplyFwdMsg" dir="ltr"><span style="font-family: Calibri, sans-serif; font-size: 11pt; color: #000000;" data-mce-style="font-family: Calibri, sans-serif; font-size: 11pt; color: #000000;"><b>From:</b> forax@univ-mlv.fr <forax@univ-mlv.fr><br><b>Sent:</b> Thursday, 18 January 2024 10:28<br><b>To:</b> Viktor Klang <viktor.klang@oracle.com><br><b>Cc:</b> core-libs-dev <core-libs-dev@openjdk.java.net>; Paul Sandoz <psandoz@openjdk.java.net><br><b>Subject:</b> [External] : Re: Gatherer: spliterator characteristics are not propagated</span><div> </div></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><hr><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><span style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;"><b>From: </b>"Viktor Klang" <viktor.klang@oracle.com><br><b>To: </b>"Remi Forax" <forax@univ-mlv.fr>, "core-libs-dev" <core-libs-dev@openjdk.java.net><br><b>Sent: </b>Wednesday, January 17, 2024 8:48:07 PM<br><b>Subject: </b>Re: Gatherer: spliterator characteristics are not propagated<br></span></blockquote><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Hi Rémi,</div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">You can find some of my benches here: <a href="https://urldefense.com/v3/__https://github.com/openjdk/jdk/tree/master/test/micro/org/openjdk/bench/java/util/stream/ops/ref__;!!ACWV5N9M2RV99hQ!JJy6F9NoL6wKZQK5158up_fTRvH8X7F6JK8T7Euuf8vzbSQbr23eWa9S_yb61ksONVrLrdesCF_au5zyje2l$" id="OWA080e8c5b-35bc-94d6-7d9f-60a18ae54e23" class="OWAAutoLink" target="_blank" rel="noopener" data-mce-href="https://urldefense.com/v3/__https://github.com/openjdk/jdk/tree/master/test/micro/org/openjdk/bench/java/util/stream/ops/ref__;!!ACWV5N9M2RV99hQ!JJy6F9NoL6wKZQK5158up_fTRvH8X7F6JK8T7Euuf8vzbSQbr23eWa9S_yb61ksONVrLrdesCF_au5zyje2l$"> https://github.com/openjdk/jdk/tree/master/test/micro/org/openjdk/bench/java/util/stream/ops/ref</a></span><br data-mce-bogus="1"></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Initially I had Characteristics such as ORDERED etc on Gatherer but it just didn't end up worth it when looking at the bench results over a wide array of stream sizes and number of operations.</div></blockquote><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">I think there are 3 gatherer characteristics that make sense: KEEP_SORTED, KEEP_DISTINCT and KEEP_SIZED,</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">all of them say that if the stream was sorted/distinct/sized then the stream returned by a call to gather() is still sorted (with the same comparator), distinct or sized.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">As examples, map() is KEEP_SIZED, filter() is KEEP_SORTED | KEEP_DISTINCT and windowFixed is KEEP_DISTINCT.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">[CC Paul, so he can correct me if i'm saying something stupid]</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">Now for the benchmarks, it depends what you want to measure, benchmarking streams is tricky. This is what i know about benchmarking streams.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">First, the JIT has two ways to profile types at runtime,</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">Either a method takes a function as parameter</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">  void map(Function function) {</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">    function.apply(...)</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">  }</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">and when map is called with a subtype of Function, the JIT will propagate the exact type when map is inlined,</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">Or a method use a field</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">  class Op {</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">    Function function;</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">    void map() {</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">       function.apply(...)</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">    }</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">  }</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">in that case, the VM records the profile of function.apply() and if there are more than two different profiles, the VM declare profile poluttion and do not try to optimize.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">The Stream implementation tries very hard to use only parameters instead of fields, that's why it does not use classical Iterator that are pull iterator (a filter iterator requires a field) but a Spliterator which is a push iterator, the element is sent as parameter of the consumer.That's also why collect does not use the builder pattern (that accumulate values in fields) but a Collector that publish the functions to be called as parameter.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">Obvisously, this is more complex than that, a Collector stores the functions in fields so it should not work well but the implementation uses a record that plays well with escape analysis. Escape analysis see the fields of an instance as parameters so the functions of a Collector are correctly propagated (if the escape analysis works). And lambdas are using invokedynamic, and the VM tries really hard to inline invokedynamic, so lambdas (that captures value or not) are routinely fully inlined with the intermediate operation of a stream.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">In your tests, i've not seen comparaisons between an existing method like map() or filter() followed by a sorted()/distinct()/count()/toList(), i.e. operations where the characteristics KEEP_* have an impact and their equivalent using a Gatherer.</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div id="x_x_Signature"><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Cheers,<br>√</div></div></blockquote><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">regards,</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;">Rémi</span></div><div><span style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></span></div><blockquote style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;" data-mce-style="margin-left: 5px; padding-left: 5px; border-left: 2px solid #1010ff;"><div id="x_x_Signature"><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><br></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><b><br></b></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;"><b>Viktor Klang</b></div><div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: #000000;">Software Architect, Java Platform Group<br>Oracle</div></div><hr style="display: inline-block; width: 98%;" data-mce-style="display: inline-block; width: 98%;"><div id="x_x_divRplyFwdMsg" dir="ltr"><span style="font-family: Calibri, sans-serif; font-size: 11pt; color: #000000;" data-mce-style="font-family: Calibri, sans-serif; font-size: 11pt; color: #000000;"><b>From:</b> core-libs-dev <core-libs-dev-retn@openjdk.org> on behalf of Remi Forax <forax@univ-mlv.fr><br><b>Sent:</b> Wednesday, 17 January 2024 16:48<br><b>To:</b> core-libs-dev <core-libs-dev@openjdk.java.net><br><b>Subject:</b> Gatherer: spliterator characteristics are not propagated</span><div> </div></div><div><span style="font-family: Helvetica, Arial, sans-serif; font-size: 11pt; color: #000000;" data-mce-style="font-family: Helvetica, Arial, sans-serif; font-size: 11pt; color: #000000;">While doing some benchmarking of the Gatherer API, i've found that the characteristics of the spliterator was not propagated by the method Stream.gather() making the stream slower than it should.<br><br>As an example, there is no way when reimplementing map() using a Gatherer to say that this intermediate operation keep the size, which is important if the terminal operation is toList() because if the size is known, toList() will presize the List and avoid the creation of an intermediary ArrayList.<br><br>See <a href="https://urldefense.com/v3/__https://github.com/forax/we_are_all_to_gather/blob/master/src/main/java/com/gihtub/forax/wearealltogather/bench/MapGathererBenchmark.java__;!!ACWV5N9M2RV99hQ!JJy6F9NoL6wKZQK5158up_fTRvH8X7F6JK8T7Euuf8vzbSQbr23eWa9S_yb61ksONVrLrdesCF_auzwTY8aB$" id="OWA4df2922d-e2cf-d3f2-96d5-6a217e6e8f51" class="OWAAutoLink" target="_blank" rel="noopener" data-mce-href="https://urldefense.com/v3/__https://github.com/forax/we_are_all_to_gather/blob/master/src/main/java/com/gihtub/forax/wearealltogather/bench/MapGathererBenchmark.java__;!!ACWV5N9M2RV99hQ!JJy6F9NoL6wKZQK5158up_fTRvH8X7F6JK8T7Euuf8vzbSQbr23eWa9S_yb61ksONVrLrdesCF_auzwTY8aB$"> https://github.com/forax/we_are_all_to_gather/blob/master/src/main/java/com/gihtub/forax/wearealltogather/bench/MapGathererBenchmark.java</a><br><br>I think that adding a way to propagate the spliterator characteristics through a Gatherer would greatly improve the performance of commons streams (at least all the ones that end with a call to toList).<br><br>I have some idea of how to do that, but I prefer first to hear if i've overlook something and if improving the performance is something worth changing the API.<br><br>regards,<br>Rémi</span></div><span style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;"><br></span></blockquote><span style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;" data-mce-style="font-family: Helvetica, Arial, sans-serif; font-size: 12pt; color: #000000;"><br></span></blockquote><br></blockquote></div></div></body></html>