Java
Java 8
String.chars()
Streams
Integers

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.

java
String s = "ABC";
s.chars().forEach(v -> System.out.println(v));
// 65, 66, 67

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:

  1. Primitive stream specialization in Java 8 includes IntStream, LongStream, and DoubleStream, but no CharStream.
  2. Many text-processing APIs in Java already use int for 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.

java
String s = "A🙂B";
s.chars().forEach(v -> System.out.println(Integer.toHexString(v)));

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.
java
1String s = "A🙂B";
2
3System.out.println("chars:");
4s.chars().forEach(v -> System.out.println(Integer.toHexString(v)));
5
6System.out.println("codePoints:");
7s.codePoints().forEach(v -> System.out.println(Integer.toHexString(v)));

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 char semantics.

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.

java
1String input = "Hi🙂世界";
2
3String letters = input.codePoints()
4    .filter(Character::isLetter)
5    .collect(StringBuilder::new,
6             StringBuilder::appendCodePoint,
7             StringBuilder::append)
8    .toString();
9
10System.out.println(letters);

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 char always equals one user-visible character.
  • Using chars() for Unicode-aware character counting.
  • Forgetting that emoji may produce two UTF-16 code units.
  • Treating IntStream values from chars() as guaranteed code points.
  • Optimizing iteration style before validating Unicode correctness.

Summary

  • String.chars() returns UTF-16 code units as an IntStream.
  • int is used because Java has primitive IntStream, not CharStream.
  • 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.

Course illustration
Course illustration

All Rights Reserved.