【问题标题】:Simplest way to print an `IntStream` as a `String`将“IntStream”打印为“String”的最简单方法
【发布时间】:2013-11-28 12:36:40
【问题描述】:

使用 Java-8,我可以使用charscodePoints 方法轻松地将String(或任何CharSequence)视为IntStream

IntStream chars = "Hello world.".codePoints();

然后我可以操作流的内容

IntStream stars = chars.map(c -> c == ' ' ? ' ': '*');

我一直在寻找一种整洁的方法来打印结果,但我什至找不到简单的方法。如何将 ints 流放回可以像 String 一样打印的表单。

来自以上stars希望打印

***** ******

【问题讨论】:

  • 顺便说一句 - 这是this 答案发展的一个阶段。我会欢迎那里的cmets。

标签: java java-8 java-stream


【解决方案1】:
String result = "Hello world."
  .codePoints()
//.parallel()  // uncomment this line for large strings
  .map(c -> c == ' ' ? ' ': '*')
  .collect(StringBuilder::new,
           StringBuilder::appendCodePoint, StringBuilder::append)
  .toString();

但是,"Hello world.".replaceAll("[^ ]", "*") 仍然更简单。并非所有东西都能从 lambdas 中受益。

【讨论】:

  • 对此我很高兴。我同意你关于过度使用 lambdas 的观点——我的例子只是一个例子。
  • 我知道,我就是无法抗拒。更复杂的字符转换甚至可以从并行执行中受益……
  • 这一点都不简单。这类东西应该从 Java 中移除,变得更加简单
  • @GeorgiosPligoropoulos 这里不适合讨论 Java 应该是什么。
  • @GeorgiosPligoropoulos Stackoverflow 仍然不适合进行此类讨论
【解决方案2】:

Holger 的效率稍低但更简洁的解决方案:

String result = "Hello world."
    .codePoints()
    .mapToObj(c -> c == ' ' ? " ": "*")
    .collect(Collectors.joining());

Collectors.joining() 内部使用StringBuilder,至少在OpenJDK sources 中。

【讨论】:

  • 不错!谢谢你——当然更整洁了。
  • 为什么效率会降低?
  • @MaartenBodewes 因为它在加入之前将每个字符转换为字符串。
  • 嗯,好的。您如何看待我的解决方案:stackoverflow.com/a/40731255/589259 我不经常使用流。
  • customTag = secureRandom.ints(9, 0, chrs.length()).mapToObj(i -> chrs.charAt(i)).collect(Collectors.joining()); 产生 IDE 编译错误 The method collect(Collector<? super Character,A,R>) in the type Stream<Character> is not applicable for the arguments (Collector<CharSequence,capture#1-of ?,String>)
【解决方案3】:

其他答案显示了如何将字符串流收集到单个字符串中,以及如何从IntStream 中收集字符。这个答案展示了如何在字符流上使用自定义收集器。

如果您想将整数流收集到字符串中,我认为最干净和最通用的解决方案是创建一个静态实用程序方法, 返回一个收集器。然后你可以像往常一样使用Stream.collect 方法。

这个实用程序可以这样实现和使用:

public static void main(String[] args){
    String s = "abcacb".codePoints()
        .filter(ch -> ch != 'b')
        .boxed()
        .collect(charsToString());

    System.out.println("s: " + s); // Prints "s: acac"
}

public static Collector<Integer, ?, String> charsToString() {
    return Collector.of(
        StringBuilder::new,
        StringBuilder::appendCodePoint,
        StringBuilder::append,
        StringBuilder::toString);
}

在标准库中没有这样的东西有点令人惊讶。

这种方法的一个缺点是它需要对字符进行装箱,因为IntStream 接口不适用于收集器。

一个未解决的难题是实用程序方法应该命名。收集器实用方法的约定是称它们为toXXX,但toString 已被占用。

【讨论】:

  • 我的解决方案收集字符串但ints。事实上,它使用与您的解决方案几乎完全相同的操作。唯一的区别是它不需要装箱,正确处理补充代码点并且更紧凑。没有为此目的的内置收集器的原因正是您提到的缺点:由于IntStream 不支持Collectors,它需要装箱。另一方面,如果您不介意装箱开销,使用Collectors.joining() 的解决方案就足够了。
  • @Holger:在工作Collectors.joining 时,您必须将字符包装成字符串,这会稍微贵一些,并且不能清楚地表达意图。使用 int 流时,您不能使用收集器。这为collector-on-boxed-chars 方法提供了一些理由。展示一种通常有用的技术也可能是一个好主意。
  • 展示替代方法很好,但您的第一句话“Other answers show how to collect a stream of strings ...”仍然是错误的。对于问题的范围,装箱到Integer 或映射到Strings 之间没有区别。后者映射到 Lukasz Wiktor 的解决方案中的两个 constant 字符串之一,并且您的代码将使用来自强制 Integer 缓存的实例,因为您只使用 ASCII 字符。对于任意字符,映射到字符串可能比装箱到整数实例稍微贵一些,但如果你关心性能,你会避免两者。
  • @Holger:修正答案的第一句话可能是个好主意。
【解决方案4】:

有一个简单的答案是不太倾向于使用流媒体来做所有事情。因此,它不是单行的,但它可能更高效且易于阅读:

public static String stars(String t) {
    StringBuilder sb = new StringBuilder(t.length());
    t.codePoints().map(c -> c == ' ' ? ' ' : '*').forEach(sb::appendCodePoint);
    return sb.toString();
}

有时候short不等于concise,我想没有人想知道上面的函数是如何运作的。

此解决方案确保代码点永远不会转换回字符。因此,它比此处列出的其他一些解决方案更通用。

【讨论】:

  • 这个解决方案没问题。但是,它有一个缺点 - 它还没有为并行流做好准备。
  • forEachOrdered 会解决这个问题吗?
【解决方案5】:

如果必须,我们可以用这种极其丑陋的方式制作单线:

public static String stars(String t) {
    return t.codePoints().map(c -> c == ' ' ? ' ': '*').mapToObj(i -> new String(new int[] { i }, 0, 1)).collect(Collectors.joining());
}

它执行与my other answer 完全相同的版本,但它一直使用流媒体。显然需要一些将单个代码点转换为字符串的函数:

public static String stars(String t) {
    return t.codePoints().map(c -> c == ' ' ? ' ': '*').mapToObj(Stars::codePointToString).collect(Collectors.joining());
}

private static String codePointToString(int codePoint) {
    return new String(new int[] { codePoint }, 0, 1);
}

当然,这会将这些函数放在 Stars 类中。

【讨论】:

    【解决方案6】:

    我这样做的方式是使用 reduce。要获得每个字符的 * 和每个空格的空格,它看起来像这样

        String hello = "hello world";
        String result = hello.chars()
            .map(val -> (val == ' ') ?  ' ' : '*')
            .mapToObj(val -> String.valueOf((char) val))
            .reduce("",(s1, s2) -> s1+s2);
    

    关键是用 integers 做任何你想做的事情,而不是将其转换为 characters 然后将其映射到 String 然后 concatenate 它,你可以使用 reduceCollectors.joining()

    【讨论】:

      【解决方案7】:

      您可以直接使用以下代码:-

      "Hello world".codePoints().forEach(n -> System.out.print(n == ' ' ? ' ':'*'));
      

      【讨论】:

      • 是的,但我认为整个想法是先将其转换为字符串,然后将其打印(转换为字符串是潜在的、最有趣的问题)。再说一次,我想你可以使用 StringWriter 实例。
      【解决方案8】:

      这个怎么样。

        System.out.println(stars.mapToObj(c -> String.format("%c", c)).collect(
                  Collectors.joining()));
      
        or
      
        String s = stars.mapToObj(c -> String.format("%c", c)).reduce("",
                  (a, b) -> a + b);
      

      【讨论】:

        【解决方案9】:
          IntStream charStream = "hello".chars();
          charStream.mapToObj(c -> (char)c).forEach(System.out::println);
        

        这对我有用。

        【讨论】:

          【解决方案10】:

          你可以这样做:

          chars.mapToObj(c -> c == ' ' ?  " ": "*").collect(joining());
          

          另一个例子:

          以下示例返回原始字符串。但这些可以与其他中间操作如filter() 结合使用。

          chars.mapToObj(i -> String.valueOf((char) i)).collect(Collectors.joining()));
          

          "abc".chars().mapToObj(i -> "" + (char) i)).collect(Collectors.joining()));
          

          【讨论】:

            猜你喜欢
            • 2018-08-29
            • 2020-05-10
            • 2014-10-27
            • 1970-01-01
            • 1970-01-01
            • 2021-11-11
            相关资源
            最近更新 更多