> Any stream can be "indexed", having an index is not an > intrisinc property of a stream of List, so adding an > index is more an intermediate operation than a source > operation. > > So more like > list.stream().withIndex() > than like > list.streamWithIndex()
I will start of by conceding this point -- this idea is much better than mine. > Could you give an example of why it is more error prone ? It is error-prone because you are mixing indexing logic with predicate logic. Those are 2 separate tasks that should be done separately. If anything, it is for this exact reason why your suggestion of .withIndex() is better than my .streamWithIndex() -- it separates the streaming from the indexing. Users grab what they need as the need arises. > Correctness is not an issue athat's why i've used > ofSequential() when creating the Gatherer. > You are right that performance can be an issue, the > Gatherer API is more limited compared to what a > Spliterator can do. > > This custom Gatherer can be declared in Gatherers (with > an 's' at the end). If your intent is to toss this into Gatherers, then I sort of see your point. But is this a good fit for Gatherers? I thought the entire point of Gatherers was to provide intermediate logic that is difficult to provide via normal streaming features? If anything, I feel like .withIndex() would be the feature to add to Gatherers. But I think it is too good of a method to put into Gatherers. It should be on the Stream base class, as you suggested. > Technically, here, you want the result to be an index, so > you may not want to pay the price of creating an index > (your WithIndex objetcs) for every elements but just one > when the predicate is true. > This point is moot if the WithIndex is a value type and > Stream is a specialized generics but as I said above, > where are not yet at that point. Ok yes, I also concede that there is missing details in my original suggestion. Like you said, I am depending on features that aren't here yet, but this problem is here and now. Valhalla when? > With your proposal, you still need to write something like > list > .withIndexStream() > .filter(withIndex -> predicate(withIndex.element())) > .mapToInt(WithIndex::index) > .findFirst() > .orElse(-1) Come on Rémi, give me some credit here. If we are making a WithIndex object, you think it is a simple no-body record? You think nothing else in the ecosystem will change in response to this inclusion? Try this instead. I will also include your suggestion to use .withIndex(). list .stream() .withIndex() .filter(WithIndex.filter(predicate)) .mapToIndex() .findFirst() .orElse(-1) On Fri, Apr 19, 2024 at 5:47 PM <fo...@univ-mlv.fr> wrote: > > > ------------------------------ > > *From: *"David Alayachew" <davidalayac...@gmail.com> > *To: *"Remi Forax" <fo...@univ-mlv.fr> > *Cc: *"ІП-24 Олександр Ротань" <rotan.olexa...@gmail.com>, > "core-libs-dev" <core-libs-dev@openjdk.org> > *Sent: *Friday, April 19, 2024 11:02:12 PM > *Subject: *Re: Addition of Predicate-based findIndex and findLastIndex > methods to java.util.List > > > No Rémi, I don't think your idea is the right approach. You are working on > the wrong level of abstraction. > > > Hello David, > > > Many users ask requests like this all the time, and what you are > suggesting would be even more error-prone than the equivalent for loop or the > IntStream suggestion that the user above requested. > > > Could you give an example of why it is more error prone ? > > Not to mention that getting it to parallelize would be a task many users > are likely to mess up -- either in correctness or in performance. > > > Correctness is not an issue athat's why i've used ofSequential() when > creating the Gatherer. > You are right that performance can be an issue, the Gatherer API is more > limited compared to what a Spliterator can do. > > > I think you would get far more mileage from adding 2 methods on the list > interface streamWithIndex() and parallelStreamWithIndex() that would return > a Stream<WithIndex<T>>, as opposed to just Stream<T>. > > > Any stream can be "indexed", having an index is not an intrisinc property > of a stream of List, so adding an index is more an intermediate operation > than a source operation. > So more like > list.stream().withIndex() > than like > list.streamWithIndex() > > Now, we (the lambda-util expert group) talked 10 years ago to add such > design but decide to postpone it because WithIndex<T> should be a value > type and the Stream API should be specialized to avoid unecessary > allocations. > So you are right that withIndex() is a more general design but because of > the performance issue we can not yet offer such design. > > As a counter argument, you can say that we have the same issue with the > Collector API and the Gatherer API which are both not specialized but at > least for a Collector, the fact that the accumulator does a side effect > which is usualyl a write in memory is slow anyway. And this is also true > for a Gatherer, it works well when the Gatherer is itself followed by a > Collector. > > > That way, users are not writing a custom Gatherer each time they want to > work with the index. They just have the index be a field in the object. > They can work with it the same way they would any other object field. > > > This custom Gatherer can be declared in Gatherers (with an 's' at the end). > > > Furthermore, doing it this way makes the correct answer obvious. If I need > to do something with an index, stream with the index. > > > Technically, here, you want the result to be an index, so you may not want > to pay the price of creating an index (your WithIndex objetcs) for every > elements but just one when the predicate is true. > This point is moot if the WithIndex is a value type and Stream is a > specialized generics but as I said above, where are not yet at that point. > > > On top of that, it significantly enhances readability by making it clear > to the reader that, whatever this stream is doing will require use of the > index, so watch out for that. > > > With your proposal, you still need to write something like > > list.withIndexStream().filter(withIndex -> > predicate(withIndex.element())).mapToInt(WithIndex::index).findFirst().orElse(-1) > > I do not see the enhancement in readability. > > regards, > Rémi > > > > > > On Fri, Apr 19, 2024, 1:47 PM Remi Forax <fo...@univ-mlv.fr> wrote: > >> Hello, >> for me, it seems what you want is Collector on Stream which is able to >> short-circuit, >> so you can write >> list.stream().collect(Collectors.findFirst(s -> s.contains("o"))) >> and in reverse >> list.reversed().stream().collect(Collectors.findFirst(s -> >> s.contains("o"))) >> >> Using a Stream here is more general and will work with other collections >> like a LinkedHashSet for example. >> Sadly, there is no way to define a short-circuiting collector :( >> >> You can have a short-circuiting Gatherer like this >> <T> Gatherer<T, ?, Integer> findIndex(Predicate<? super T> predicate) { >> return Gatherer.ofSequential( >> () -> new Object() { int index; }, >> Integrtor.ofGreedy((state, element, downstream) -> { >> var index = state.index++; >> if (predicate.test(element)) { >> return downstream.push(index); >> } >> return true; >> })); >> } >> >> and use it like this: >> list.stream().gather(findIndex(s -> >> s.contains("o"))).findFirst().orElse(-1); >> >> But it's more verbose. >> >> I wonder if at the same time that the Gatherer API is introduced, the >> Collector API should be enhanced to support short-circuiting collectors ? >> >> regards, >> Rémi >> >> >> ------------------------------ >> >> *From: *"ІП-24 Олександр Ротань" <rotan.olexa...@gmail.com> >> *To: *"core-libs-dev" <core-libs-dev@openjdk.org> >> *Sent: *Friday, April 19, 2024 5:59:39 PM >> *Subject: *Addition of Predicate-based findIndex and findLastIndex >> methods to java.util.List >> >> Subject >> Addition of Predicate-based findIndex and findLastIndex methods to >> java.util.List >> >> Motivation >> 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. >> >> 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. >> >> The changes I am proposing are implemented in this PR: >> https://github.com/openjdk/jdk/pull/18639. Here is a brief overview of >> the changes made in this pull request: >> >> Added the findIndex (Predicate<? super E> filter) method to the List >> interface. >> Added the findLastIndex (Predicate<? super E> filter) method to the List >> interface. >> Implemented these methods in all non-abstract classes that implement the >> List interface, as well as List itself (default impl). >> 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. >> >> For example, consider the following test case: >> >> List<String> list = new ArrayList<>(); >> list.add("Object one"); >> list.add("NotObject two"); >> list.add("NotObject three"); >> >> int index1 = list.findIndex(s -> s.contains("ct t")); >> System.out.println(index1); // Expected output: 1 >> int index2 = list. findLastIndex(s -> s.startsWith("NotObject")); >> System.out.println(index2); // Expected output: 2 >> Currently, to achieve the same result, we would have to use a more >> verbose approach: >> >> int index1 = IntStream.range(0, list.size()) >> .filter(i -> list.get(i).contains("ct t")) >> .findFirst() >> .orElse(-1); >> System.out.println(index1); // Output: 1 >> int index2 = IntStream.range(0, list.size()) >> .filter(i -> list.get(i).startsWith("NotObject")) >> .reduce((first, second) -> second) >> .orElse(-1); >> System.out.println(index2); // Output: 2 >> Or other approaches that require additional instructions and, therefore, >> can`t be used in all scopes (like passing argument to method). >> >> 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. >> >> 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! >> >> Best regards >> >> >