Why is String.chars a stream of ints in Java 8?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
String.chars() returning IntStream surprises many Java developers because strings are often thought of as sequences of char. The design is intentional and tied to both Unicode handling and Stream API type choices. Once you separate UTF-16 code units from Unicode code points, the API behavior becomes much clearer.
chars() Returns UTF-16 Code Units
In Java, String stores text in UTF-16 representation. A Java char is a 16-bit UTF-16 code unit, not a full Unicode character in all cases.
String.chars() iterates those code units and returns each as an int value.
The method does not claim to return full Unicode code points. It returns units of the underlying UTF-16 representation.
Why int Instead of char
Two practical reasons:
- Primitive stream specialization in Java 8 includes
IntStream,LongStream, andDoubleStream, but noCharStream. - Many text-processing APIs in Java already use
intfor character-related values, especially code points.
Returning IntStream avoids boxing overhead from Stream<Character> and aligns with existing Unicode APIs that use integer code values.
Supplementary Characters and Surrogate Pairs
Characters outside the Basic Multilingual Plane use two UTF-16 code units, called a surrogate pair. With chars(), you see those two units separately.
You will observe one value for A, two surrogate values for the emoji, and one for B.
If your task is character semantics across all Unicode code points, use codePoints().
chars() Versus codePoints()
Use the method that matches your intent:
chars()for UTF-16 code-unit processing.codePoints()for true Unicode code-point processing.
codePoints() merges surrogate pairs into one integer value, which is usually what you want for internationalized text logic.
Choosing the Right API by Use Case
Prefer chars() when:
- You are doing low-level UTF-16-aware operations.
- You intentionally inspect code units.
- You work with legacy logic built around
charsemantics.
Prefer codePoints() when:
- You validate user-perceived characters.
- You process emoji or supplementary scripts.
- You implement Unicode-correct indexing and filtering.
Most user-facing text logic should use codePoints() to avoid incorrect splitting of supplementary characters.
Example: Unicode-Safe Letter Filtering
Incorrect approach with code units can break for supplementary characters. Better approach is code-point based filtering.
This preserves full code points and avoids surrogate corruption.
Performance Considerations
IntStream primitive specialization keeps processing efficient compared with boxed streams. For hot loops, plain indexed loops can still outperform streams, but stream pipelines improve readability for many transformation tasks.
If performance is critical:
- Benchmark with representative multilingual text.
- Compare stream and loop implementations.
- Validate correctness with supplementary characters first, then optimize.
Correct Unicode handling is usually more important than micro-optimizing code-unit iteration.
Common Pitfalls
- Assuming one Java
charalways equals one user-visible character. - Using
chars()for Unicode-aware character counting. - Forgetting that emoji may produce two UTF-16 code units.
- Treating
IntStreamvalues fromchars()as guaranteed code points. - Optimizing iteration style before validating Unicode correctness.
Summary
String.chars()returns UTF-16 code units as anIntStream.intis used because Java has primitiveIntStream, notCharStream.- Supplementary characters appear as surrogate pairs in
chars()output. - Use
codePoints()for Unicode-correct character-level logic. - Pick the API based on semantics first, then optimize performance.

