Java 使用流操作字符串

Java 使用流操作字符串,java,string,lambda,java-8,java-stream,Java,String,Lambda,Java 8,Java Stream,假设我想从字符串中删除所有非字母 String s = "abc-de3-2fg"; 我可以使用IntStream来实现这一点: s.stream().filter(ch -> Character.isLetter(ch)). // But then what? 要将此流转换回字符串实例,我可以做些什么 另一方面,为什么我不能将字符串视为字符类型的对象流 String s = "abc-de3-2fg"; // Yields a Stream of char[], therefor

假设我想从
字符串中删除所有非字母

String s = "abc-de3-2fg";
我可以使用
IntStream
来实现这一点:

s.stream().filter(ch -> Character.isLetter(ch)).  // But then what?
要将此流转换回
字符串
实例,我可以做些什么

另一方面,为什么我不能将
字符串
视为
字符
类型的对象流

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

当然,这还不够好。。。我缺少什么?

方法
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);

不幸的是,Java8流API严重支持这种场景。我的库添加了两个助手方法来处理这些流:,和。此外,我还介绍了一些原语收集器,例如,它们可以帮助以某种非平凡的方式收集原语流

以下是如何使用StreamEx库解决您的任务:

String result = IntStreamEx.ofChars(s).filter(Character::isLetter).charsToString();
或使用代码点:

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

下面是问题第二部分的答案。如果调用
string.chars()
生成了
IntStream
,则可以通过强制转换到
char
,然后调用
mapToObj
来装箱结果,从而获得
流。例如,下面介绍如何将
字符串
转换为
集合

如果显示的字符串包含代码点U+1D400(数学粗体大写字母a),则该操作将失败。该代码点在字符串中表示为代理项对,代理项对的值都不是字母字符。要获得正确的结果,您需要执行以下操作:

string.codePoints()
      .filter(Character::isAlphabetic)
      ...
我建议始终使用
codePoints()

现在,给定一个
IntStream
代码点,如何将其重新组合成字符串?是合理的(+1),使用
IntStream
的三个参数
collect()
方法

这里有一个替代方案:

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

如果您已经有了一个
StringBuilder
用于积累字符串数据,那么这可能会更灵活一些。您不必每次都创建一个新的
StringBuilder
,也不必在以后将其转换为
String

很抱歉,但这看起来非常冗长(我不熟悉java 1.8)。难道没有更简洁的东西吗?也许用一个reduce方法?使用Scala,您可以编写类似于
“abc-de3-2fg”.filter(Isleter).mkString(“”)的代码,在Scala中,字符串上的过滤器是字符串,这里不是这种情况,chars()在这里返回IntStream,IntStream上的过滤器返回IntStream,有一个名为collector.joining()的简化方法但这需要将每个字符转换成字符串,我只是感到惊讶。我刚刚做了几次尝试来提供一个更简洁的解决方案,但没有成功(使用mapping、toArray和其他一些东西)。我是Java的支持者,但Scala如此受欢迎也就不足为奇了。@FrancisToth总是有
int[]filtered=s.codePoints().filter(Character::isleter.toArray();s=新字符串(已过滤,0,已过滤.length)。不需要收集器。@FrancisToth基本上,尝试将流转换为
字符串
被视为边缘大小写。(在某些方面,这是合理的。)我试着玩了一点StreamEx,看起来很不错。我试试看:-)@kidclemper,很高兴听到这个消息。如果您有任何增强建议或发现错误,请随时联系。将此答案标记为IMO认可,这是最优雅的解决方案,不需要第三方库
string.chars()
      .filter(Character::isAlphabetic)
      ...
string.codePoints()
      .filter(Character::isAlphabetic)
      ...
StringBuilder sb = ... ;
string.codePoints()
      .filter(...)
      .forEachOrdered(sb::appendCodePoint);
return sb.toString();