ZetCode

Java CharSequence Interface

Last modified: April 13, 2025

The java.lang.CharSequence interface represents a readable sequence of char values. It provides uniform, read-only access to different types of character sequences. This interface is implemented by String, StringBuilder, StringBuffer, and other character sequence classes.

CharSequence was introduced in Java 1.4 to create a common abstraction for character sequences. It defines basic operations for examining characters without modifying them. Many Java APIs accept CharSequence to work with different character sequence types flexibly.

CharSequence Interface Methods

The CharSequence interface defines several methods for examining characters. These methods must be implemented by all classes that implement the interface. The main methods include length, charAt, subSequence, and toString.

public interface CharSequence {
    int length();
    char charAt(int index);
    CharSequence subSequence(int start, int end);
    String toString();
}

The code above shows the methods defined by the CharSequence interface. These methods provide basic read-only access to character sequences. The interface does not include any mutation operations.

Basic CharSequence Usage

This example demonstrates basic usage of CharSequence with different implementations. We show how to use the interface methods with String, StringBuilder, and StringBuffer.

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        // Different CharSequence implementations
        CharSequence str = "Hello World";
        CharSequence sb = new StringBuilder("Java Programming");
        CharSequence sbf = new StringBuffer("CharSequence Demo");
        
        // Common operations
        System.out.println("String length: " + str.length());
        System.out.println("5th char in StringBuilder: " + sb.charAt(4));
        System.out.println("Subsequence of StringBuffer: " + 
                         sbf.subSequence(0, 11));
        
        // toString() usage
        String s = str.toString();
        System.out.println("Converted to String: " + s);
    }
}

This example shows how CharSequence provides a common interface for different character sequence types. We can call the same methods regardless of the underlying implementation. The toString method converts any CharSequence to a String when needed.

CharSequence with String

Strings are the most common CharSequence implementation. This example shows specific String operations through the CharSequence interface.

Main.java
package com.zetcode;

public class Main {
    public static void processSequence(CharSequence seq) {
        System.out.println("Processing sequence: " + seq);
        System.out.println("Length: " + seq.length());
        System.out.println("First char: " + seq.charAt(0));
        System.out.println("Last char: " + seq.charAt(seq.length() - 1));
    }
    
    public static void main(String[] args) {
        String text = "Programming in Java";
        processSequence(text);
        processSequence(text.subSequence(0, 11));
    }
}

Here we demonstrate how String works as a CharSequence. The processSequence method accepts any CharSequence, allowing it to work with both the full String and its subsequence. This shows the flexibility of programming to interfaces.

CharSequence with StringBuilder

StringBuilder is a mutable CharSequence implementation. This example shows how to use StringBuilder through the CharSequence interface while maintaining mutability.

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("Initial Value");
        CharSequence cs = sb;
        
        System.out.println("Original: " + cs);
        System.out.println("Length: " + cs.length());
        System.out.println("Char at 3: " + cs.charAt(3));
        
        // Mutate the underlying StringBuilder
        sb.append(" Appended");
        System.out.println("After mutation: " + cs);
        
        // Create subsequence
        CharSequence sub = cs.subSequence(8, 13);
        System.out.println("Subsequence: " + sub);
    }
}

This example demonstrates that while CharSequence itself is read-only, the underlying StringBuilder remains mutable. Changes to the StringBuilder are visible through the CharSequence reference. The subsequence operation creates a new CharSequence view of part of the original.

Comparing CharSequence Implementations

This example compares different CharSequence implementations and shows how to work with them polymorphically. We'll process various sequence types through a common interface.

Main.java
package com.zetcode;

public class Main {
    public static void printSequenceInfo(CharSequence seq) {
        System.out.println("Type: " + seq.getClass().getSimpleName());
        System.out.println("Content: " + seq);
        System.out.println("Length: " + seq.length());
        
        if (seq.length() > 0) {
            System.out.println("First char: " + seq.charAt(0));
            System.out.println("Last char: " + 
                             seq.charAt(seq.length() - 1));
        }
        
        System.out.println("-----");
    }
    
    public static void main(String[] args) {
        CharSequence[] sequences = {
            "String implementation",
            new StringBuilder("StringBuilder implementation"),
            new StringBuffer("StringBuffer implementation"),
            new java.nio.CharBuffer() {
                public char get() { return 'X'; }
                public char get(int index) { return 'X'; }
                public int length() { return 10; }
                public CharSequence subSequence(int s, int e) { return this; }
                public String toString() { return "Custom Implementation"; }
            }
        };
        
        for (CharSequence seq : sequences) {
            printSequenceInfo(seq);
        }
    }
}

This example shows how to handle different CharSequence implementations through a single interface. We process String, StringBuilder, StringBuffer, and even a custom implementation. The printSequenceInfo method works with all types transparently, demonstrating interface polymorphism.

CharSequence in Regular Expressions

Java's regular expression API extensively uses CharSequence. This example shows pattern matching with different CharSequence implementations.

Main.java
package com.zetcode;

import java.util.regex.*;

public class Main {

    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("\\d{3}-\\d{2}-\\d{4}");
        
        CharSequence[] inputs = {
            "123-45-6789", // String
            new StringBuilder("987-65-4321"),
            new StringBuffer("Invalid-12-3456"),
            "Another invalid 12-34-5678"
        };
        
        for (CharSequence input : inputs) {
            Matcher matcher = pattern.matcher(input);
            System.out.println(input + ": " + 
                             (matcher.matches() ? "Valid" : "Invalid"));
        }
    }
}

This example demonstrates how Java's regex API accepts any CharSequence. We test different implementations against a social security number pattern. The Pattern and Matcher classes work with all CharSequence types, showing the interface's utility in API design.

Custom CharSequence Implementation

This example shows how to create a custom CharSequence implementation. We'll build a simple sequence that reverses another CharSequence.

Main.java
package com.zetcode;

public class ReverseCharSequence implements CharSequence {
    private final CharSequence original;
    
    public ReverseCharSequence(CharSequence original) {
        this.original = original;
    }
    
    @Override
    public int length() {
        return original.length();
    }
    
    @Override
    public char charAt(int index) {
        return original.charAt(original.length() - 1 - index);
    }
    
    @Override
    public CharSequence subSequence(int start, int end) {
        return new ReverseCharSequence(
            original.subSequence(original.length() - end, 
                               original.length() - start));
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(length());
        for (int i = 0; i < length(); i++) {
            sb.append(charAt(i));
        }
        return sb.toString();
    }
}

public class Main {

    public static void main(String[] args) {
        CharSequence original = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        CharSequence reversed = new ReverseCharSequence(original);
        
        System.out.println("Original: " + original);
        System.out.println("Reversed: " + reversed);
        System.out.println("5th char: " + reversed.charAt(4));
        System.out.println("Subseq(5-10): " + 
                         reversed.subSequence(5, 10));
    }
}

This example demonstrates creating a custom CharSequence that reverses another sequence. The ReverseCharSequence class implements all required methods, providing a view of the original sequence in reverse order. This shows how CharSequence can be used to create specialized sequence views.

CharSequence Performance Considerations

This example compares performance characteristics of different CharSequence implementations for common operations.

Main.java
package com.zetcode;

public class Main {
    public static void measurePerformance(CharSequence seq, String type) {
        final int ITERATIONS = 1000000;
        
        // Measure charAt performance
        long start = System.nanoTime();
        for (int i = 0; i < ITERATIONS; i++) {
            seq.charAt(i % seq.length());
        }
        long duration = System.nanoTime() - start;
        System.out.printf("%s charAt: %d ns%n", type, duration/ITERATIONS);
        
        // Measure subSequence performance
        start = System.nanoTime();
        for (int i = 0; i < ITERATIONS; i++) {
            seq.subSequence(0, seq.length() / 2);
        }
        duration = System.nanoTime() - start;
        System.out.printf("%s subSequence: %d ns%n", type, duration/ITERATIONS);
    }
    
    public static void main(String[] args) {
        CharSequence str = "PerformanceTestString";
        CharSequence sb = new StringBuilder("PerformanceTestString");
        CharSequence sbf = new StringBuffer("PerformanceTestString");
        
        measurePerformance(str, "String");
        measurePerformance(sb, "StringBuilder");
        measurePerformance(sbf, "StringBuffer");
    }
}

This example measures the performance of basic CharSequence operations across different implementations. String typically offers the fastest read operations, while StringBuilder and StringBuffer may have different performance characteristics. Results may vary based on JVM implementation and version.

Source

Java CharSequence Interface Documentation

In this article, we've covered the Java CharSequence interface with practical examples. Understanding CharSequence is valuable for writing flexible APIs that can work with different character sequence types efficiently.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.