<div dir="ltr">Hello there! As I see, your approach finds all matching indexes, not just first. This would be much more suitable using enumerated streams. Discussion about them has been moved to another thread (see EnumeratedStream subject in mails).<div><br></div><div> Few messages above I provided benchmarks indicating that for one element, list-based approach drastically outperforms any stream-based, so if you would like to contribute something to enumerated streams discussion specifically, you are welcome, but I don't think such dramatic performance loss is worth the flexibility your approach provides.</div><div><br></div><div>However, a gatherer-based enumerated streams approach has been discussed, and your implementation looks really concie. I like it.</div><div><br></div><div>PS: little offtop regarding enumerated streams. In compiler-dev, I have proposed to introduce extension methods (workin implementation is already present). Those extensions could really ease Stream API development as well as many others and also provide a more comfortable interface for Java users. However, the compiler maintenance team seems to not even consider my proposal (or at least I didn't get any feedback besides that it was rejected previously, even though I provided some argumentation about why pros outweigh cons of such proposal). If any core-libs-dev mainaiter or enthusiast feels like such feature could be helpful for them, I encourage you to state that in a related topic so that the compiler team knows there is demand for extension methods.Thanks for your time!</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">вт, 23 апр. 2024 г. в 15:57, Viktor Klang <<a href="mailto:viktor.klang@oracle.com">viktor.klang@oracle.com</a>>:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="msg-5708701956887795838">
<div dir="ltr">
<blockquote style="margin-left:0.8ex;padding-left:1ex;border-left:3px solid rgb(200,200,200)">
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
Sadly, there is no way to define a short-circuiting collector :(</div>
</blockquote>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
That was my first course of exploration for JEP-461, and unfortunately I found no great way of retrofitting that behavior, especially not in a backwards-compatibility-safe way.</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<blockquote style="margin-left:0.8ex;padding-left:1ex;border-left:3px solid rgb(200,200,200)">
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
You can have a short-circuiting Gatherer like this</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<T> Gatherer<T, ?, Integer> findIndex(Predicate<? super T> predicate) {<br>
return Gatherer.ofSequential(<br>
() -> new Object() { int index; },<br>
Integrtor.ofGreedy((state, element, downstream) -> {<br>
var index = state.index++;<br>
if (predicate.test(element)) {<br>
return downstream.push(index);<br>
}<br>
return true;<br>
}));<br>
}</div>
</blockquote>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
Since this would ne a short-circuiting Gatherer, it would not qualify as Greedy.</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
With that said, you could create something like:<br>
<br>
</div>
<div style="background-color:rgb(31,31,31)">
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px">
<span style="color:rgb(86,156,214)">public</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(86,156,214)">static</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(212,212,212)"><</span><span style="color:rgb(204,204,204)">T, R</span><span style="color:rgb(212,212,212)">></span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(78,201,176)">Gatherer</span><span style="color:rgb(212,212,212)"><</span><span style="color:rgb(204,204,204)">T,
</span><span style="color:rgb(197,134,192)">?</span><span style="color:rgb(204,204,204)">, Long</span><span style="color:rgb(212,212,212)">></span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(220,220,170)">indiciesWhere</span><span style="color:rgb(204,204,204)">(</span><span style="color:rgb(78,201,176)">Predicate</span><span style="color:rgb(212,212,212)"><</span><span style="color:rgb(204,204,204)">T</span><span style="color:rgb(212,212,212)">></span><span style="color:rgb(204,204,204)">
p) {</span></div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px">
<span style="color:rgb(204,204,204)"> </span><span style="color:rgb(86,156,214)">class</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(78,201,176)">Index</span><span style="color:rgb(204,204,204)"> {
</span><span style="color:rgb(78,201,176)">long</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(156,220,254)">at</span><span style="color:rgb(204,204,204)">; }</span></div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px">
<span style="color:rgb(204,204,204)"> </span><span style="color:rgb(197,134,192)">return</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(156,220,254)">Gatherer</span><span style="color:rgb(204,204,204)">.</span><span style="color:rgb(220,220,170)">ofSequential</span><span style="color:rgb(204,204,204)">(</span></div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px">
<span style="color:rgb(204,204,204)"> Index</span><span style="color:rgb(197,134,192)">::new</span><span style="color:rgb(204,204,204)">,</span></div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px">
<span style="color:rgb(204,204,204)"> </span><span style="color:rgb(156,220,254)">Integrator</span><span style="color:rgb(204,204,204)">.</span><span style="color:rgb(220,220,170)">ofGreedy</span><span style="color:rgb(204,204,204)">((idx,
e, d) </span><span style="color:rgb(86,156,214)">-></span><span style="color:rgb(204,204,204)"> {</span></div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px">
<span style="color:rgb(204,204,204)"> </span><span style="color:rgb(86,156,214)">var</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(156,220,254)">current</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(212,212,212)">=</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(156,220,254)">idx</span><span style="color:rgb(204,204,204)">.</span><span style="color:rgb(156,220,254)">at</span><span style="color:rgb(212,212,212)">++</span><span style="color:rgb(204,204,204)">;</span></div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px">
<span style="color:rgb(204,204,204)"> </span><span style="color:rgb(197,134,192)">return</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(212,212,212)">!</span><span style="color:rgb(156,220,254)">p</span><span style="color:rgb(204,204,204)">.</span><span style="color:rgb(220,220,170)">test</span><span style="color:rgb(204,204,204)">(e)
</span><span style="color:rgb(212,212,212)">||</span><span style="color:rgb(204,204,204)">
</span><span style="color:rgb(156,220,254)">d</span><span style="color:rgb(204,204,204)">.</span><span style="color:rgb(220,220,170)">push</span><span style="color:rgb(204,204,204)">(current);</span></div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:rgb(204,204,204)">
})</div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:rgb(204,204,204)">
);</div>
<div style="line-height:18px;white-space:pre-wrap;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:rgb(204,204,204)">
}</div>
</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
So you could do: list.stream().gather(indiciesWhere(predicate)).findFirst()</div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div id="m_-5708701956887795838Signature" style="color:inherit;background-color:inherit">
<div style="font-family:Calibri,Arial,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
Cheers,<br>
√</div>
<div style="font-family:Calibri,Arial,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div style="font-family:Calibri,Arial,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<b><br>
</b></div>
<div style="font-family:Calibri,Arial,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<b>Viktor Klang</b></div>
<div style="font-family:Calibri,Arial,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
Software Architect, Java Platform Group<br>
Oracle</div>
</div>
<div id="m_-5708701956887795838appendonsend" style="color:inherit;background-color:inherit"></div>
<div style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<hr style="display:inline-block;width:98%">
<div id="m_-5708701956887795838divRplyFwdMsg" dir="ltr" style="color:inherit;background-color:inherit">
<span style="font-family:Calibri,sans-serif;font-size:11pt;color:rgb(0,0,0)"><b>From:</b> core-libs-dev <<a href="mailto:core-libs-dev-retn@openjdk.org" target="_blank">core-libs-dev-retn@openjdk.org</a>> on behalf of Remi Forax <<a href="mailto:forax@univ-mlv.fr" target="_blank">forax@univ-mlv.fr</a>><br>
<b>Sent:</b> Friday, 19 April 2024 19:47<br>
<b>To:</b> ІП-24 Олександр Ротань <<a href="mailto:rotan.olexandr@gmail.com" target="_blank">rotan.olexandr@gmail.com</a>><br>
<b>Cc:</b> core-libs-dev <<a href="mailto:core-libs-dev@openjdk.org" target="_blank">core-libs-dev@openjdk.org</a>><br>
<b>Subject:</b> Re: Addition of Predicate-based findIndex and findLastIndex methods to java.util.List</span>
<div> </div>
</div>
<div id="m_-5708701956887795838x_zimbraEditorContainer" style="font-family:arial,helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0);background-color:inherit">
<div>Hello,</div>
<div>for me, it seems what you want is Collector on Stream which is able to short-circuit,</div>
<div>so you can write</div>
<div> list.stream().collect(Collectors.findFirst(s -> s.contains("o")))</div>
<div>and in reverse</div>
<div> list.reversed().stream().collect(Collectors.findFirst(s -> s.contains("o")))</div>
<div> </div>
<div>Using a Stream here is more general and will work with other collections like a LinkedHashSet for example.</div>
<div>Sadly, there is no way to define a short-circuiting collector :(</div>
<div><br>
</div>
<div>You can have a short-circuiting Gatherer like this</div>
<div> <T> Gatherer<T, ?, Integer> findIndex(Predicate<? super T> predicate) {<br>
return Gatherer.ofSequential(<br>
() -> new Object() { int index; },<br>
Integrtor.ofGreedy((state, element, downstream) -> {<br>
var index = state.index++;<br>
if (predicate.test(element)) {<br>
return downstream.push(index);<br>
}<br>
return true;<br>
}));<br>
}</div>
<div><br>
</div>
<div>and use it like this:</div>
<div> list.stream().gather(findIndex(s -> s.contains("o"))).findFirst().orElse(-1);</div>
<div><br>
</div>
<div>But it's more verbose.</div>
<div><br>
</div>
<div>I wonder if at the same time that the Gatherer API is introduced, the Collector API should be enhanced to support short-circuiting collectors ?</div>
<div><br>
</div>
<div>regards,</div>
<div>Rémi</div>
<div><br>
</div>
<div><br>
</div>
<hr>
<blockquote style="margin-left:5px;padding-left:5px;border-left:2px solid rgb(16,16,255)">
<span style="font-family:Helvetica,Arial,sans-serif;font-size:12pt;color:rgb(0,0,0)"><b>From:
</b>"ІП-24 Олександр Ротань" <<a href="mailto:rotan.olexandr@gmail.com" target="_blank">rotan.olexandr@gmail.com</a>><br>
<b>To: </b>"core-libs-dev" <<a href="mailto:core-libs-dev@openjdk.org" target="_blank">core-libs-dev@openjdk.org</a>><br>
<b>Sent: </b>Friday, April 19, 2024 5:59:39 PM<br>
<b>Subject: </b>Addition of Predicate-based findIndex and findLastIndex methods to java.util.List<br>
</span></blockquote>
<blockquote style="margin-left:5px;padding-left:5px;border-left:2px solid rgb(16,16,255)">
<div style="direction:ltr;font-family:Helvetica,Arial,sans-serif;font-size:12pt;color:rgb(0,0,0)">
Subject<br>
Addition of Predicate-based findIndex and findLastIndex methods to java.util.List<br>
<br>
Motivation<br>
The motivation behind this proposal is to enhance the functionality of the List interface by providing a more flexible way to find the index of an element. Currently, the indexOf and lastIndexOf methods only accept an object as a parameter. This limits the
flexibility of these methods as they can only find the index of exact object matches.<br>
<br>
Here I want to propose methods that would accept a Predicate as a parameter, allowing users to define a condition that the desired element must meet. This would provide a more flexible and powerful way to find the index of an element in a list.<br>
<br>
The changes I am proposing are implemented in this PR: <a href="https://github.com/openjdk/jdk/pull/18639" id="m_-5708701956887795838OWA137c83e3-2257-866b-5b51-191fe4640b7d" target="_blank">
https://github.com/openjdk/jdk/pull/18639</a>. Here is a brief overview of the changes made in this pull request:<br>
<br>
Added the findIndex (Predicate<? super E> filter) method to the List interface.<br>
Added the findLastIndex (Predicate<? super E> filter) method to the List interface.<br>
Implemented these methods in all non-abstract classes that implement the List interface, as well as List itself (default impl).<br>
The changes have been thoroughly tested to ensure they work as expected and do not introduce any regressions. The test cases cover a variety of scenarios to ensure the robustness of the implementation.<br>
<br>
For example, consider the following test case:<br>
<br>
List<String> list = new ArrayList<>();<br>
list.add("Object one");<br>
list.add("NotObject two");<br>
list.add("NotObject three");<br>
<br>
int index1 = list.findIndex(s -> s.contains("ct t"));<br>
System.out.println(index1); // Expected output: 1<br>
int index2 = list. findLastIndex(s -> s.startsWith("NotObject"));<br>
System.out.println(index2); // Expected output: 2<br>
Currently, to achieve the same result, we would have to use a more verbose approach:<br>
<br>
int index1 = IntStream.range(0, list.size())<br>
.filter(i -> list.get(i).contains("ct t"))<br>
.findFirst()<br>
.orElse(-1);<br>
System.out.println(index1); // Output: 1<br>
int index2 = IntStream.range(0, list.size())<br>
.filter(i -> list.get(i).startsWith("NotObject"))<br>
.reduce((first, second) -> second)<br>
.orElse(-1);<br>
System.out.println(index2); // Output: 2</div>
<div style="direction:ltr;font-family:Helvetica,Arial,sans-serif;font-size:12pt;color:rgb(0,0,0)">
Or other approaches that require additional instructions and, therefore, can`t be used in all scopes (like passing argument to method).</div>
<div style="direction:ltr;font-family:Helvetica,Arial,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
I believe these additions would greatly enhance the functionality and flexibility of the List interface, making it more powerful and user-friendly. I look forward to your feedback and am open to making any necessary changes based on your suggestions.<br>
<br>
The main reason I am publishing this proposal in the mailing system is to gather feedback from the Java developers community, especially about possible caveats related to backward compatibility of your projects. Would appreciate every opinion!</div>
<div style="direction:ltr;font-family:Helvetica,Arial,sans-serif;font-size:12pt;color:rgb(0,0,0)">
<br>
</div>
<div style="direction:ltr;font-family:Helvetica,Arial,sans-serif;font-size:12pt;color:rgb(0,0,0)">
Best regards</div>
<span style="font-family:Helvetica,Arial,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></blockquote>
</div>
</div>
</div></blockquote></div>