Почему мне нужно привязать IntStream к Stream <Character> - PullRequest
6 голосов
/ 11 июня 2019
  public static int construction(String myString) {
      Set<Character> set = new HashSet<>();

      int count = myString.chars()  // returns IntStream
      .mapToObj(c -> (char)c)       // Stream<Character> why is this required?
      .mapToInt(c -> (set.add(c) == true ? 1 : 0)) // IntStream
      .sum();

      return count;
    }

Приведенный выше код не будет компилироваться без:

.mapObj(c -> (char)c)
// <Character> Stream<Character> java.util.stream.IntStream.mapToObj(IntFunction<? extends Character> mapper)

Если я удалю его, я получу следующую ошибку

The method mapToInt((<no type> c) -> {}) is undefined for the type IntStream

Может кто-нибудь объяснить это?Похоже, я начинаю с IntStream и преобразую его в поток символов, а затем возвращаюсь в IntStream.

Ответы [ 3 ]

7 голосов
/ 11 июня 2019

Метод CharSequence::chars возвращает IntStream, что, конечно, не обеспечивает какой-либо метод преобразования в int, например mapToInt, но mapToObj вместо,Следовательно, метод IntStream::map(IntUnaryOperator mapper), который также принимает возврат int, должен использоваться, так как IntUnaryOperator делает то же самое, что и Function<Integer, Integer> или UnaryOperator<Integer>:

int count = myString.chars()                 // IntStream
    .map(c -> (set.add((char) c) ? 1 : 0))   // IntStream
    .sum();

long count = myString.chars()                // IntStream
    .filter(c -> set.add((char) c))          // IntStream
    .count();

Кроме того, использование Set<Integer> помогает избежать преобразования в символ:

Set<Integer> set = new HashSet<>();

int count = myString.chars()                 // IntStream
    .map(c -> (set.add(c) ? 1 : 0))          // IntStream
    .sum();

long count = myString.chars()                // IntStream
    .filter(set::add)                        // IntStream
    .count();

Однако, независимо от того, чего вы пытаетесь достичь, ваш кодошибочно по принципу, НЕТ безгражданства , если быть точным.Рассмотрите возможность использования следующего фрагмента, в котором результаты лямбда-выражений не зависят от результата недетерминированной операции, такой как Set::add.

Результаты конвейерного потока могут быть недетерминированнымиили неверный, если поведенческие параметры для операций потока являются состоящими.

long count = myString.chars()             // IntStream
                     .distinct()          // IntStream
                     .count();
2 голосов
/ 11 июня 2019

Вы также можете собрать в набор, а затем взять размер без использования явной карты.Не требуется использовать внешнее состояние для хранения символов.

    long count = str.chars().boxed().collect(Collectors.toSet()).size();

Но, imho, более прямой подход, который уже упоминался, выглядит чище и тот, который я предпочел бы использовать.

    long count = str.chars().distinct().count();
1 голос
/ 11 июня 2019

Поскольку String.chars() уже возвращает IntStream, а IntStream не имеет mapToInt function

Вместо этого можно использовать фильтр, а затем считать:

int count = myString.chars()
      .filter(c -> set.add(c) == true)
      .count();

Я признаю, что я сделал это так грубо прошлой полуночи!Как отмечено в комментариях, здесь есть необходимые исправления.

Спасибо, что упомянули.

long count = myString.chars()
          .filter(c -> set.add((char)c))
          .count();
...