Использование потоков для управления строкой

Скажем, что я хочу удалить все не-буквы из моего String.

String s = "abc-de3-2fg";

Я могу использовать IntStream для этого:

s.stream().filter(ch -> Character.isLetter(ch)).  // But then what?

Что я могу сделать, чтобы преобразовать этот поток в экземпляр String?

В другой заметке, почему я не могу рассматривать String как поток объектов типа Character?

String s = "abc-de3-2fg";

// Yields a Stream of char[], therefore doesn't compile
Stream<Character> stream = Stream.of(s.toCharArray());

// Yields a stream with one member - s, which is a String object. Doesn't compile
Stream<Character> stream = Stream.of(s);

Согласно javadoc, подпись создания Stream выглядит следующим образом:

Поток (значения T...)

Единственный (паршивый) способ, о котором я мог думать, это:

String s = "abc-de3-2fg";
Stream<Character> stream = Stream.of(s.charAt(0), s.charAt(1), s.charAt(2), ...)

И, конечно, это не достаточно хорошо... Что мне не хватает?

Ответ 1

Здесь ответ второй части вопроса. Если у вас есть IntStream, вызванный вызовом string.chars(), вы можете получить Stream<Character>, выполнив кастинг на char, а затем поместите результат, вызвав mapToObj. Например, здесь, как превратить a String в Set<Character>:

Set<Character> set = string.chars()
    .mapToObj(ch -> (char)ch)
    .collect(Collectors.toSet());

Обратите внимание, что для t23 > важно, чтобы результат с коротким значением был Character вместо Integer.

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

(Может показаться, что дополнительные символы - это неясная функция Юникода, о которой нам не нужно беспокоиться, но, насколько я знаю, все эможи являются дополнительными символами.)

Рассмотрим следующий пример:

string.chars()
      .filter(Character::isAlphabetic)
      ...

Это будет fail, если будет представлена ​​строка, содержащая кодовую точку U + 1D400 (Математическая жирная столица A). Эта кодовая точка представляется в виде суррогатной пары в строке, и ни значение суррогатной пары не является буквенным символом. Чтобы получить правильный результат, вам нужно сделать это вместо:

string.codePoints()
      .filter(Character::isAlphabetic)
      ...

Я рекомендую всегда использовать codePoints().

Теперь, учитывая IntStream кодовых точек, как можно собрать его в строку? Ответ Sleiman Jneidi является разумным (+1), используя метод collect() > .

Здесь альтернатива:

StringBuilder sb = ... ;
string.codePoints()
      .filter(...)
      .forEachOrdered(sb::appendCodePoint);
return sb.toString();

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

Ответ 2

Метод chars возвращает IntStream. Вам просто не хватает коллектора

String s = "abc-de3-2fg";
String s1 = s.chars().filter(Character::isLetter)
            .collect(StringBuilder::new,StringBuilder::appendCodePoint,StringBuilder::append)
            .toString();
System.out.println(s1);

Ответ 3

К сожалению, такой сценарий плохо поддерживается Java 8 Stream API. Моя библиотека StreamEx добавляет несколько вспомогательных методов для работы с такими потоками: IntStreamEx.charsToString(), IntStreamEx.codePointsToString() и IntStreamEx.toCharArray(). Также я представил примитивных коллекционеров, таких как IntCollector, которые могут помочь собрать примитивные потоки некоторым нетривиальным способом.

Здесь, как ваша задача может быть решена с помощью библиотеки StreamEx:

String result = IntStreamEx.ofChars(s).filter(Character::isLetter).charsToString();

Или с кодовыми точками:

String result = IntStreamEx.ofCodePoints(s)
                           .filter(Character::isLetter)
                           .codePointsToString();