使用流来操作String

时间:2015-08-12 23:57:41

标签: java string lambda java-8 java-stream

我们想要删除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的创建签名如下:

  

Stream.of(T ...值)

我能想到的唯一(糟糕的)方式是:

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

当然,这还不够好......我错过了什么?

4 个答案:

答案 0 :(得分:10)

这是问题第二部分的答案。如果您通过调用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

现在处理charCharacter数据的一个大问题是,补充字符表示为char值的代理对,所以任何算法都是处理个别char值时,如果出现补充字符,可能会失败。

(看起来补充字符是一个不起眼的Unicode功能,我们不需要担心,但据我所知,所有表情符号都是补充字符。)

考虑这个例子:

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

如果显示包含代码点U + 1D400(数学大胆资本A)的字符串,则失败。该代码点表示为字符串中的代理项对,并且代理项对的值都不是字母字符。要获得正确的结果,您需要改为:

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

我建议始终使用codePoints()

现在,给定IntStream个代码点,如何将其重组为String? Sleiman Jneidi's answer是合理的(+1),使用collect()的三参数IntStream方法。

这是另一种选择:

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

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

答案 1 :(得分:7)

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

答案 2 :(得分:4)

不幸的是,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();

答案 3 :(得分:0)

public String removeNonLetters(String value) {
    return value.chars()
            .mapToObj(i -> (char) i)               // map int to char
            .filter(Character::isLetter)           // filter non-letters
            .map(String::valueOf)                  // required for joining
            .collect(Collectors.joining());
}