假设我要从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
的创建签名如下:我能想到的唯一(糟糕)方法是:
String s = "abc-de3-2fg";
Stream<Character> stream = Stream.of(s.charAt(0), s.charAt(1), s.charAt(2), ...)
当然,这还不够好……我想念的是什么?
最佳答案
这是问题第二部分的答案。如果您有一个通过调用IntStream
生成的string.chars()
,则可以通过转换为Stream<Character>
然后通过调用char
将结果装箱来获得mapToObj
。例如,以下是将String
转换为Set<Character>
的方法:
Set<Character> set = string.chars()
.mapToObj(ch -> (char)ch)
.collect(Collectors.toSet());
请注意,强制转换为
char
对于将装箱结果为Character
而不是Integer
是必不可少的。现在,处理
char
或Character
数据的最大问题是,补充字符被表示为char
值的替代对,因此任何处理单个char
值的算法都可能在出现补充字符时失败。(补充字符似乎是我们不需要担心的晦涩的Unicode功能,但是据我所知,所有表情符号都是补充字符。)
考虑以下示例:
string.chars()
.filter(Character::isAlphabetic)
...
如果提供包含代码点U + 1D400(数学粗体A)的字符串,则将失败,而将失败。该代码点在字符串中表示为代理对,而代理对的值都不是字母字符。为了获得正确的结果,您需要这样做:
string.codePoints()
.filter(Character::isAlphabetic)
...
我建议始终使用
codePoints()
。现在,给定一个
IntStream
的代码点,如何将其重新组装为String? Sleiman Jneidi's answer使用collect()
的三参数IntStream
方法是合理的(+1)。这是一个替代方案:
StringBuilder sb = ... ;
string.codePoints()
.filter(...)
.forEachOrdered(sb::appendCodePoint);
return sb.toString();
如果您已经有一个用来存储字符串数据的
StringBuilder
,这可能会更灵活一些。您不必每次都创建一个新的StringBuilder
,也不必随后将其转换为String
。