Sadly, there is no way to define a short-circuiting collector :(

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.

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;
      }));
  }

Since this would ne a short-circuiting Gatherer, it would not qualify as Greedy.

With that said, you could create something like:

public static <T, R> Gatherer<T, ?, Long> indiciesWhere(Predicate<T> p) {
       class Index { long at; }
       return Gatherer.ofSequential(
               Index::new,
               Integrator.ofGreedy((idx, e, d) -> {
                 var current = idx.at++;
                 return !p.test(e) || d.push(current);
               })
       );
}


So you could do: list.stream().gather(indiciesWhere(predicate)).findFirst()

Cheers,
√


Viktor Klang
Software Architect, Java Platform Group
Oracle

________________________________
From: core-libs-dev <core-libs-dev-r...@openjdk.org> on behalf of Remi Forax 
<fo...@univ-mlv.fr>
Sent: Friday, 19 April 2024 19:47
To: ІП-24 Олександр Ротань <rotan.olexa...@gmail.com>
Cc: core-libs-dev <core-libs-dev@openjdk.org>
Subject: Re: Addition of Predicate-based findIndex and findLastIndex methods to 
java.util.List

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

Reply via email to