Java 8/9: Может ли символ в String сопоставляться с его индексами (используя потоки)?

Учитывая String s и char c, мне любопытно, существует ли какой-либо метод создания List<Integer> list из s (где элементы в list представляют индексы c в s).

Близким, но неправильным подходом было бы:

public static List<Integer> getIndexList(String s, char c) {
    return s.chars()
            .mapToObj(i -> (char) i)
            .filter(ch -> ch == c)
            .map(s::indexOf) // Will obviously return the first index every time.
            .collect(Collectors.toList());
}

Следующие входы должны давать следующий результат:

getIndexList("Hello world!", 'l') -> [2, 3, 9]

Ответ 1

Может быть сделано с IntStream

public static List<Integer> getIndexList(String s, char c) {
    return IntStream.range(0, s.length())
                    .filter(index -> s.charAt(index) == c)
                    .boxed()
                    .collect(Collectors.toList());
}

Ответ 2

Используя Java 9, вы можете итеративно искать с использованием последнего индекса в качестве начальной точки и останавливаться, если совпадение не найдено:

public static List<Integer> getIndexList(String s, char c) {
    return IntStream.iterate(s.indexOf(c), i -> s.indexOf(c, i + 1))
            .takeWhile(i -> i > -1)
            .boxed()
            .collect(Collectors.toList());
}

Отказ от ответственности: я не проверял это.

Ответ 3

Альтернативой Java9 может быть использование iterate(int seed, IntPredicate hasNext,IntUnaryOperator next) следующим образом:

private static List<Integer> getIndexList(String word, char c) {
  return IntStream
          .iterate(word.indexOf(c), index -> index >= 0, index -> word.indexOf(c, index + 1))
          .boxed()
          .collect(Collectors.toList());
}

Ответ 4

... Или в java-9:

Stream.of("Hello world!")
            .map(Scanner::new)
            .flatMap(s -> s.findAll("l"))
            .map(mr -> mr.start())
            .forEach(System.out::println);