【问题标题】:Java 8 Streams: IntStream to String [duplicate]Java 8 Streams:IntStream 到 String [重复]
【发布时间】:2015-02-02 15:09:09
【问题描述】:

在 Java 8 流 API 中,对任何 String 对象调用 chars() 会返回一个包含所有字符的 IntStream 对象。

将返回的IntStream 对象转换回String 的正确方法是什么?调用 toArray() 会给我一个 int[],它不被任何 String 构造函数接受。

【问题讨论】:

  • 哪个类包含chars()方法?
  • 方法在CharSequence接口中定义,String实现。
  • chars().mapToObj(i -> (char) i).map(String::valueOf).collect(Collectors.joining());

标签: java java-8 java-stream


【解决方案1】:

使用StringBuilderappendCodePoint 方法也可以解决问题,

IntStream in = "Convert me to a String".codePoints();

String intStreamToString = in.collect(StringBuilder::new,
        StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();

System.out.println(intStreamToString);

【讨论】:

  • 是的,但是这个也有@jon-skeet指出的代码单元/代码点不匹配的问题。 chars() 返回代码单元的流,而appendCodePoint 期望得到代码点,顾名思义。
  • 问题是StringBuilder对象会创建很多次。
  • @Namo 不,只会创建一个StringBuilder,请参阅docs
  • @cdalxndr 你说得对!我很困惑。 begin method 在 TerminalOp 中只调用了一次。
【解决方案2】:

您可以使用toArray(),然后使用String(int[], int, int) 构造函数。这并不完全令人满意,因为 chars() 被指定返回 UTF-16 代码单元,基本上:

返回一个 int 流,对该序列中的 char 值进行零扩展。任何映射到代理代码点的字符都会未经解释地传递。

使用codePoints() 会更符合这个构造函数,它需要代码点而不是 UTF-16 代码单元。否则(使用chars)如果你的原始字符串确实包含代理对,你可能会发现你得到一个错误 - 我没有尝试过,但它会有意义。

我不知道不先转换为数组的简单方法。

【讨论】:

  • 好的。使用codePoints() 而不是chars() 似乎更安全,尽管在我的情况下这无关紧要,因为我只是在ASCII 范围内工作。无论如何,谢谢。
【解决方案3】:

我很确定一定有很多方法可以做到这一点,但另一种方法是使用StringWriter

IntStream in = "It was the best of times".chars();
StringWriter sw = new StringWriter();
in.forEach(sw::write); 
System.out.println(sw.toString());

这一切也可以在收集器中表示为:

IntStream in = "It was the best of times".chars();
String text = in.collect(
    StringWriter::new, 
    StringWriter::write, 
    (swl, swr) -> swl.write(swr.toString())).toString();
System.out.println(text);

【讨论】:

  • 没有想过将它与 IO 库结合起来。谢谢。
  • @nullstein 它也可以写成收藏家,正如我的版本所示,尽管我怀疑它是否更容易阅读。从好的方面来说,它确实具有更好的引用透明度,因为副作用对流用户隐藏。不过,这最终类似于 StringBuilder 的答案。
  • 请注意,StringWriter 是 StringBuffer(StringBuilder 的同步版本)的包装器,因此(相对)比使用 StringBuilder 慢得多
【解决方案4】:

这是另一个想法:

@Test
public void testIntStreamSequential() {
    final String testString = "testmesoftly";
    IntStream is = testString.chars();
    String result = is.collect(
        StringBuilder::new,
        (sb, i) -> sb.append((char)i),
        StringBuilder::append
        ).toString();
    assertEquals(testString, result);
}

@Test
public void testIntStreamParallel() {
    final String testString = "testmesoftly";
    IntStream is = testString.chars();
    String result = is.parallel().collect(
        StringBuilder::new,
        (sb, i) -> sb.append((char)i),
        StringBuilder::append
        ).toString();
    assertEquals(testString, result);
}

请注意,使用@Lii 提出的专用Collector 效率不高,因为拳击所以你应该使用这三个参数构造(感谢@holger)

【讨论】:

  • 其实我也想过这个方向,但是好像太复杂了。不过,我有一个快速的问题。为什么第三个参数是(sb1, sb2) -> {}?这到底是什么意思?这部分根本不用吗?也许只有在使用并行流时才相关?
  • 它是组合器,“一个接受两个部分结果并将它们合并的函数。”。使用顺序时不使用它。但是我的例子很丑,所以我改进了它并删除了关于并行支持的警告
  • IntStream 不支持专用的Collectors,因此使用三参数collect 方法是唯一的选择(除非您想对每个int 进行装箱...)。跨度>
  • @holger 你是对的!编辑了我的答案
猜你喜欢
  • 1970-01-01
  • 2020-03-17
  • 2020-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-31
  • 2018-08-02
相关资源
最近更新 更多