Simplest way to print an `IntStream` as a `String`
JavaJava 8Java StreamJava Problem Overview
With Java-8 I can easily treat a String
(or any CharSequence
) as an IntStream
using either the chars
or the codePoints
method.
IntStream chars = "Hello world.".codePoints();
I can then manipulate the contents of the stream
IntStream stars = chars.map(c -> c == ' ' ? ' ': '*');
I have been hunting for a tidy way to print the results and I cant even find a simple way. How do I put this stream of int
s back into a form that can be printed like I can a String
.
From the above stars
I hope to print
***** ******
Java Solutions
Solution 1 - Java
String result = "Hello world."
.codePoints()
//.parallel() // uncomment this line for large strings
.map(c -> c == ' ' ? ' ': '*')
.collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
But still, "Hello world.".replaceAll("[^ ]", "*")
is simpler. Not everything benefits from lambdas.
Solution 2 - Java
A bit less efficient but more concise solution to Holger's:
String result = "Hello world."
.codePoints()
.mapToObj(c -> c == ' ' ? " ": "*")
.collect(Collectors.joining());
Collectors.joining()
internally uses StringBuilder
, at least in OpenJDK sources.
Solution 3 - Java
Other answers show how to collect a stream of strings into a single string and how to collect characters from an IntStream
. This answer shows how to use a custom collector on a stream of characters.
If you want to collect a stream of ints into a string, I think the cleanest and most general solution is to create a static utility method that
returns a collector. Then you can use the Stream.collect
method as usual.
This utility can be implemented and used like this:
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);
}
It's a bit surprising that there isn't something like this in the standard library.
One disadvantage of this approach is that it requires the chars to be boxed since the IntStream
interface does not work with collectors.
An unsolved and hard problem is what the utility method should be named. The convention for collector utility methods is to call them toXXX
, but toString
is already taken.
Solution 4 - Java
There is a simple answer that is slightly less inclined to doing everything with streaming. Hence it is not a one-liner, but it is probably more efficient and very easy to read:
public static String stars(String t) {
StringBuilder sb = new StringBuilder(t.length());
t.codePoints().map(c -> c == ' ' ? ' ' : '*').forEach(sb::appendCodePoint);
return sb.toString();
}
Sometimes short is not the same as concise, I don't think anybody has to wonder how the above function operates.
This solution makes sure that the code points are never converted back to characters. It is therefore somewhat more generic than some other solutions listed here.
Solution 5 - Java
If we must, we can make a one-liner this extremely ugly way:
public static String stars(String t) {
return t.codePoints().map(c -> c == ' ' ? ' ': '*').mapToObj(i -> new String(new int[] { i }, 0, 1)).collect(Collectors.joining());
}
It performs the exact same version as my other answer but it uses streaming all the way. Some function to convert a single code point to a string is obviously needed:
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);
}
which places these functions in the class Stars
of course.
Solution 6 - Java
The way I do it is by using reduce. To get * for each character and space for each space it will look like this
String hello = "hello world";
String result = hello.chars()
.map(val -> (val == ' ') ? ' ' : '*')
.mapToObj(val -> String.valueOf((char) val))
.reduce("",(s1, s2) -> s1+s2);
The point is do whatever you want with the integers than cast it to characters then map it to String then concatenate it, you can use reduce or Collectors.joining()
Solution 7 - Java
You can do it directly with the following code :-
"Hello world".codePoints().forEach(n -> System.out.print(n == ' ' ? ' ':'*'));
Solution 8 - Java
How about this.
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);
Solution 9 - Java
IntStream charStream = "hello".chars();
charStream.mapToObj(c -> (char)c).forEach(System.out::println);
This works for me.
Solution 10 - Java
You can do:
chars.mapToObj(c -> c == ' ' ? " ": "*").collect(joining());
Another example:
The following examples return the original string. But these can be combined with other intermediate operations such as filter()
.
chars.mapToObj(i -> String.valueOf((char) i)).collect(Collectors.joining()));
"abc".chars().mapToObj(i -> "" + (char) i)).collect(Collectors.joining()));