As others have already mentioned, the design decision behind this was to prevent the explosion of methods and classes.

Still, personally I think this was a very bad decision, and there should, given they do not want to make CharStream, which is reasonable, different methods instead of chars(), I would think of:

  • Stream<Character> chars(), that gives a stream of boxes characters, which will have some light performance penalty.
  • IntStream unboxedChars(), which would to be used for performance code.

However, instead of focusing on why it is done this way currently, I think this answer should focus on showing a way to do it with the API that we have gotten with Java 8.

In Java 7 I would have done it like this:

for (int i = 0; i < hello.length(); i++) {
    System.out.println(hello.charAt(i));
}

And I think a reasonable method to do it in Java 8 is the following:

hello.chars()
        .mapToObj(i -> (char)i)
        .forEach(System.out::println);

Here I obtain an IntStream and map it to an object via the lambda i -> (char)i, this will automatically box it into a Stream<Character>, and then we can do what we want, and still use method references as a plus.

Be aware though that you must do mapToObj, if you forget and use map, then nothing will complain, but you will still end up with an IntStream, and you might be left off wondering why it prints the integer values instead of the strings representing the characters.

Other ugly alternatives for Java 8:

By remaining in an IntStream and wanting to print them ultimately, you cannot use method references anymore for printing:

hello.chars()
        .forEach(i -> System.out.println((char)i));

Moreover, using method references to your own method do not work anymore! Consider the following:

private void print(char c) {
    System.out.println(c);
}

and then

hello.chars()
        .forEach(this::print);

This will give a compile error, as there possibly is a lossy conversion.

Conclusion:

The API was designed this way because of not wanting to add CharStream, I personally think that the method should return a Stream<Character>, and the workaround currently is to use mapToObj(i -> (char)i) on an IntStream to be able to work properly with them.

Answer from skiwi on Stack Overflow
🌐
Medium
neesri.medium.com › string-to-a-stream-character-in-java-8-884c9b5e00f7
Convert String to a Stream<Character> in Java 8 | by A cup of JAVA coffee with NeeSri | Medium
July 20, 2025 - import java.util.stream.Collectors; ... = "Hello"; // Convert String to Stream<Character> Stream<Character> charStream = inputString.chars() .mapToObj(c -> (char) c); // Print the characters in the stream charStream.forEach...
🌐
Baeldung
baeldung.com › home › java › java string › converting string to stream of chars
Converting String to Stream of chars | Baeldung
January 8, 2024 - The String API has a new method – chars() – with which we can obtain an instance of Stream from a String object. This simple API returns an instance of IntStream from the input String.
🌐
GeeksforGeeks
geeksforgeeks.org › java › convert-string-to-stream-of-chars-java
Convert String to Stream of Chars in Java - GeeksforGeeks
January 26, 2024 - The StringReader class from the java.io package in Java can be used to convert a String to a character stream.
🌐
Java Guides
javaguides.net › 2024 › 09 › java-8-convert-string-to-stream-of-characters.html
Java 8 – Convert String to Stream of Characters
September 9, 2024 - Java 8 provides a simple and efficient way to convert a string to a stream of characters using the chars() method from the String class.
🌐
Medium
nicksamoylov.medium.com › java-streams-11-create-from-string-using-chars-codepoints-and-lines-953f85d5d7ec
Java streams 11. Create from String using chars(), codePoints(), and lines() | by Nick Samoylov | Medium
February 11, 2020 - The lines() method creates a stream of lines extracted from this string, separated by line terminators. ... The created IntStream emits code points (integer char values) of the characters.
🌐
GeeksforGeeks
geeksforgeeks.org › java › convert-string-to-char-stream-in-java-without-using-library-functions
How to Convert String to Char Stream in Java without Using Library Functions? - GeeksforGeeks
July 23, 2025 - In Java, we can convert a string ... So, we will create an empty char array iterate it with string characters, and store the individual characters from the string to the char array one after the other....
Top answer
1 of 2
267

As others have already mentioned, the design decision behind this was to prevent the explosion of methods and classes.

Still, personally I think this was a very bad decision, and there should, given they do not want to make CharStream, which is reasonable, different methods instead of chars(), I would think of:

  • Stream<Character> chars(), that gives a stream of boxes characters, which will have some light performance penalty.
  • IntStream unboxedChars(), which would to be used for performance code.

However, instead of focusing on why it is done this way currently, I think this answer should focus on showing a way to do it with the API that we have gotten with Java 8.

In Java 7 I would have done it like this:

for (int i = 0; i < hello.length(); i++) {
    System.out.println(hello.charAt(i));
}

And I think a reasonable method to do it in Java 8 is the following:

hello.chars()
        .mapToObj(i -> (char)i)
        .forEach(System.out::println);

Here I obtain an IntStream and map it to an object via the lambda i -> (char)i, this will automatically box it into a Stream<Character>, and then we can do what we want, and still use method references as a plus.

Be aware though that you must do mapToObj, if you forget and use map, then nothing will complain, but you will still end up with an IntStream, and you might be left off wondering why it prints the integer values instead of the strings representing the characters.

Other ugly alternatives for Java 8:

By remaining in an IntStream and wanting to print them ultimately, you cannot use method references anymore for printing:

hello.chars()
        .forEach(i -> System.out.println((char)i));

Moreover, using method references to your own method do not work anymore! Consider the following:

private void print(char c) {
    System.out.println(c);
}

and then

hello.chars()
        .forEach(this::print);

This will give a compile error, as there possibly is a lossy conversion.

Conclusion:

The API was designed this way because of not wanting to add CharStream, I personally think that the method should return a Stream<Character>, and the workaround currently is to use mapToObj(i -> (char)i) on an IntStream to be able to work properly with them.

2 of 2
109

The answer from skiwi covered many of the major points already. I'll fill in a bit more background.

The design of any API is a series of tradeoffs. In Java, one of the difficult issues is dealing with design decisions that were made long ago.

Primitives have been in Java since 1.0. They make Java an "impure" object-oriented language, since the primitives are not objects. The addition of primitives was, I believe, a pragmatic decision to improve performance at the expense of object-oriented purity.

This is a tradeoff we're still living with today, nearly 20 years later. The autoboxing feature added in Java 5 mostly eliminated the need to clutter source code with boxing and unboxing method calls, but the overhead is still there. In many cases it's not noticeable. However, if you were to perform boxing or unboxing within an inner loop, you'd see that it can impose significant CPU and garbage collection overhead.

When designing the Streams API, it was clear that we had to support primitives. The boxing/unboxing overhead would kill any performance benefit from parallelism. We didn't want to support all of the primitives, though, since that would have added a huge amount of clutter to the API. (Can you really see a use for a ShortStream?) "All" or "none" are comfortable places for a design to be, yet neither was acceptable. So we had to find a reasonable value of "some". We ended up with primitive specializations for int, long, and double. (Personally I would have left out int but that's just me.)

For CharSequence.chars() we considered returning Stream<Character> (an early prototype might have implemented this) but it was rejected because of boxing overhead. Considering that a String has char values as primitives, it would seem to be a mistake to impose boxing unconditionally when the caller would probably just do a bit of processing on the value and unbox it right back into a string.

We also considered a CharStream primitive specialization, but its use would seem to be quite narrow compared to the amount of bulk it would add to the API. It didn't seem worthwhile to add it.

The penalty this imposes on callers is that they have to know that the IntStream contains char values represented as ints and that casting must be done at the proper place. This is doubly confusing because there are overloaded API calls like PrintStream.print(char) and PrintStream.print(int) that differ markedly in their behavior. An additional point of confusion possibly arises because the codePoints() call also returns an IntStream but the values it contains are quite different.

So, this boils down to choosing pragmatically among several alternatives:

  1. We could provide no primitive specializations, resulting in a simple, elegant, consistent API, but which imposes a high performance and GC overhead;

  2. we could provide a complete set of primitive specializations, at the cost of cluttering up the API and imposing a maintenance burden on JDK developers; or

  3. we could provide a subset of primitive specializations, giving a moderately sized, high performing API that imposes a relatively small burden on callers in a fairly narrow range of use cases (char processing).

We chose the last one.

Find elsewhere
🌐
Oracle
docs.oracle.com › javase › tutorial › essential › io › charstreams.html
Character Streams (The Java™ Tutorials > Essential Java Classes > Basic I/O)
The Java platform stores character values using Unicode conventions. Character stream I/O automatically translates this internal format to and from the local character set.
🌐
Dirask
dirask.com › posts › Java-8-convert-char-to-Stream-Character-ZDN7Kp
Java 8 - convert char[] to Stream<Character>
char[] charArr = {'a', 'b', 'c'}; // Example 1 - the best way: Stream<Character> charStream = new String(charArr) .chars().mapToObj(c -> (char) c); ... char[] charArr = {'a', 'b', 'c'}; // Example 2 Stream<Character> charStream2 = IntStream.range(0, ...
🌐
Java2s
java2s.com › Tutorials › Java › Java_Stream › 0070__Java_Stream_From_String.htm
Java Streams - Java Stream From String
chars() from the CharSequence interface returns an IntStream whose elements are int values representing the characters. We can use chars() method on a String, a StringBuilder, and a StringBuffer. The following code creates a stream of characters from a string, filters out all digits and ...
🌐
Techie Delight
techiedelight.com › home › java › convert a string to character array in java
Convert a String to Character array in Java | Techie Delight
July 7, 2026 - A naive solution is to use a regular ... stream, but we can get an IntStream (a stream of ints) of characters using the String.chars() method when dealing with strings....
🌐
javathinking
javathinking.com › blog › convert-string-to-character-stream-java
Converting String to Character Stream in Java — javathinking.com
They operate on Unicode characters ... streams are Reader and Writer. To convert a String to a character stream, we can use the StringReader class....
🌐
Techie Delight
techiedelight.com › home › java › convert intstream to string in java and vice versa
Convert IntStream to String in Java and vice versa | Techie Delight
July 7, 2026 - Since Java doesn’t offer any character stream, we can create an IntStream (a stream of ints) of characters when dealing with strings.
🌐
Oracle
docs.oracle.com › javase › tutorial › i18n › text › stream.html
Character and Byte Streams (The Java™ Tutorials > Internationalization > Working with Text)
InputStreamReader defaultReader = new InputStreamReader(fis); String defaultEncoding = defaultReader.getEncoding(); The example that follows shows you how to perform character-set conversions with the InputStreamReader and OutputStreamWriter classes. The full source code for this example is in StreamConverter.java.
🌐
YouTube
youtube.com › watch
How to Create a Stream from the Characters of a String in Java | Streams - YouTube
Hi guys, I Aziz welcome you to CoedMaster.In this video, I talked about How to Create a Stream from the Characters of a String in Java.Feedback and Suggestio...
Published: October 18, 2023
🌐
Blogger
gjf2a.blogspot.com › 2015 › 01 › java-8-processing-character-streams.html
Computing Intelligently: Java 8: Processing character streams
January 26, 2015 - .mapToObj(StringDemo1::flipCap) .map(c -> Character.toString(c)) .reduce("", (s, c) -> s + c); } public static Character flipCap(int c) { if (c >= 'A' && c <= 'Z') { return (char)(c - 'A' + 'a'); } else if (c >= 'a' && c <= 'z') { return (char)(c - 'a' + 'A'); } else { return (char)c; } } } First, we need to convert the string to a stream.
🌐
GeeksforGeeks
geeksforgeeks.org › java › java-program-to-convert-string-to-char-stream-without-using-stream
Java Program to Convert String to Char Stream Without Using Stream - GeeksforGeeks
July 23, 2025 - String Reader class can also be used to convert a string into character array. This class can be use by importing Java.io package. Below is the implementation of converting string to char stream using String Reader Class: