CharBuffer
8 mins
The CharBuffer class is a character buffer that implements CharSequence, making it suitable for text processing operations. It provides character-level I/O operations and can be used with charset encoders/decoders.
Source Code #
Core Implementation #
public abstract class CharBuffer extends Buffer implements CharSequence, Comparable<CharBuffer> {
// Factory methods
public static CharBuffer allocate(int capacity) {
return new HeapCharBuffer(capacity, capacity);
}
public static CharBuffer wrap(CharSequence csq) {
return wrap(csq.toString());
}
public static CharBuffer wrap(char[] array) {
return new HeapCharBuffer(array, 0, array.length);
}
public static CharBuffer wrap(char[] array, int offset, int length) {
return new HeapCharBuffer(array, offset, length);
}
// CharSequence implementation
public abstract char charAt(int index);
public abstract CharSequence subSequence(int start, int end);
public abstract String toString();
// Character operations
public abstract char get();
public abstract char get(int index);
public abstract CharBuffer put(char c);
public abstract CharBuffer put(int index, char c);
// Bulk operations
public CharBuffer get(char[] dst) {
return get(dst, 0, dst.length);
}
public CharBuffer get(char[] dst, int offset, int length) {
checkBounds(offset, length, dst.length);
if (length > remaining())
throw new BufferUnderflowException();
for (int i = offset; i < offset + length; i++)
dst[i] = get();
return this;
}
// Append and read operations
public CharBuffer append(CharSequence csq) {
return put(csq.toString());
}
public CharBuffer append(char c) {
return put(c);
}
public CharBuffer append(CharSequence csq, int start, int end) {
return put(csq.subSequence(start, end).toString());
}
}
Implementation Details #
CharSequence Implementation #
CharBuffer implements CharSequence, allowing it to be used wherever CharSequence is expected:
CharBuffer buffer = CharBuffer.allocate(100);
buffer.put("Hello, World!").flip();
// CharSequence methods
char ch = buffer.charAt(5); // ','
int length = buffer.length(); // 13
CharSequence sub = buffer.subSequence(0, 5); // "Hello"
String str = buffer.toString(); // "Hello, World!"
// Can be used with APIs expecting CharSequence
StringBuilder sb = new StringBuilder();
sb.append(buffer);
Pattern pattern = Pattern.compile("Hello");
Matcher matcher = pattern.matcher(buffer);
Character Encoding and Decoding #
CharBuffer works with charset encoders and decoders:
// Encoding characters to bytes
Charset charset = StandardCharsets.UTF_8;
CharBuffer charBuffer = CharBuffer.wrap("Hello, World!");
ByteBuffer byteBuffer = charset.encode(charBuffer);
// Decoding bytes to characters
ByteBuffer source = ByteBuffer.wrap(bytes);
CharBuffer decoded = charset.decode(source);
// Direct encoding/decoding
CharsetEncoder encoder = charset.newEncoder();
CharsetDecoder decoder = charset.newDecoder();
CoderResult result = encoder.encode(charBuffer, byteBuffer, true);
if (result.isUnderflow()) {
// All input processed
}
Text Processing Operations #
// Reading lines from buffer
CharBuffer buffer = CharBuffer.allocate(1024);
// Fill buffer with text...
buffer.flip();
while (buffer.hasRemaining()) {
// Find end of line
int start = buffer.position();
int end = start;
while (end < buffer.limit() && buffer.get(end) != '\n') {
end++;
}
if (end < buffer.limit()) {
// Extract line (excluding newline)
CharBuffer line = buffer.slice(start, end - start);
buffer.position(end + 1); // Skip newline
// Process line...
}
}
Compact Operation for Text #
public CharBuffer compact() {
int pos = position();
int lim = limit();
int rem = (pos <= lim ? lim - pos : 0);
// Copy remaining characters to beginning
for (int i = 0; i < rem; i++) {
put(i, get(pos + i));
}
position(rem);
limit(capacity());
discardMark();
return this;
}
// Usage for text processing
CharBuffer buffer = CharBuffer.allocate(1024);
Reader reader = new FileReader("text.txt");
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
buffer.flip();
// Process complete lines
processLines(buffer);
// Compact remaining partial line
buffer.compact();
}
reader.close();
View Operations #
CharBuffer can be created from ByteBuffer:
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.order(ByteOrder.BIG_ENDIAN);
// Create CharBuffer view
CharBuffer charBuffer = byteBuffer.asCharBuffer();
// The view shares the underlying byte buffer
// Changes affect both buffers
charBuffer.put('A'); // Writes 2 bytes to byteBuffer
Performance Characteristics #
| Operation | HeapCharBuffer | DirectCharBuffer (via ByteBuffer) |
|---|---|---|
| Allocation | O(1) fast | O(n) slower (native allocation) |
| Character Access | O(1) direct array access | O(1) with bounds checking |
| Bulk Operations | Fast (System.arraycopy) | Slower (element-by-element) |
| I/O Operations | Requires copying | Zero-copy possible |
| Memory Overhead | Low (char[] + object) | High (native memory + view overhead) |
Common Patterns #
Text File Processing #
CharBuffer buffer = CharBuffer.allocate(8192);
FileChannel channel = FileChannel.open(path, StandardOpenOption.READ);
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
while (channel.read(byteBuffer) != -1) {
byteBuffer.flip();
// Decode bytes to characters
CoderResult result = decoder.decode(byteBuffer, buffer, false);
buffer.flip();
// Process text
while (buffer.hasRemaining()) {
char ch = buffer.get();
// Process character...
}
buffer.compact();
byteBuffer.compact();
}
// Flush decoder
decoder.decode(byteBuffer, buffer, true);
decoder.flush(buffer);
buffer.flip();
// Process remaining text
String Building with CharBuffer #
CharBuffer buffer = CharBuffer.allocate(1024);
// Append multiple strings
buffer.append("Hello");
buffer.append(", ");
buffer.append("World!");
buffer.append('\n');
// Convert to string
buffer.flip();
String result = buffer.toString();
// Reset for reuse
buffer.clear();
Pattern Matching on Buffer #
CharBuffer buffer = CharBuffer.allocate(1024);
// Fill buffer with text...
buffer.flip();
// Use regex on buffer
Pattern pattern = Pattern.compile("\\b\\w+\\b");
Matcher matcher = pattern.matcher(buffer);
while (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
CharBuffer word = buffer.slice(start, end - start);
// Process word...
}
// Reset matcher for new data
buffer.compact();
// Fill with new data...
buffer.flip();
matcher.reset(buffer);
Character Stream Processing #
class CharBufferProcessor {
private CharBuffer buffer = CharBuffer.allocate(4096);
public void process(ReadableByteChannel channel, Charset charset)
throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4096);
CharsetDecoder decoder = charset.newDecoder();
while (channel.read(byteBuffer) != -1) {
byteBuffer.flip();
boolean endOfInput = false;
while (!endOfInput) {
CoderResult result = decoder.decode(byteBuffer, buffer, false);
buffer.flip();
processBuffer(buffer);
buffer.compact();
if (result.isUnderflow()) {
break;
} else if (result.isOverflow()) {
// Buffer full, process and continue
buffer.flip();
processBuffer(buffer);
buffer.compact();
}
}
byteBuffer.compact();
}
// Flush decoder
decoder.decode(byteBuffer, buffer, true);
decoder.flush(buffer);
buffer.flip();
processBuffer(buffer);
buffer.clear();
}
private void processBuffer(CharBuffer buffer) {
// Process text in buffer...
}
}
Best Practices #
Choose Appropriate Capacity:
- For line-oriented text: 4KB-8KB buffers
- For whole document processing: match expected document size
- For streaming: 1KB-4KB buffers with compact() reuse
Use CharSequence Features:
- Leverage
CharSequenceimplementation for interoperability - Use
subSequence()for zero-copy substring extraction - Implement text algorithms directly on buffer
- Leverage
Handle Encoding Properly:
- Always specify charset when encoding/decoding
- Use
StandardCharsetsconstants for common charsets - Handle
CoderResultfor robust error handling
Reuse Buffers:
// Thread-local buffer for text processing private static final ThreadLocal<CharBuffer> TEXT_BUFFER = ThreadLocal.withInitial(() -> CharBuffer.allocate(4096)); public void processText(String text) { CharBuffer buffer = TEXT_BUFFER.get(); buffer.clear(); buffer.put(text); buffer.flip(); // Process... }Monitor Buffer Limits:
- Check
remaining()before bulk operations - Use
hasRemaining()in loops - Handle buffer overflow/underflow gracefully
- Check
Common Pitfalls #
- Forgetting to Flip: Reading from buffer after writing without
flip() - Character Encoding Issues: Assuming default platform charset
- Buffer Overflow: Writing beyond capacity without checking
remaining() - View Buffer Confusion: CharBuffer views of ByteBuffer have independent position/limit
- Line Ending Differences: Platform-specific line endings (
\n,\r\n,\r) - Unicode Surrogate Pairs: Multi-char code points may span buffer boundaries
- Thread Safety: CharBuffer is not thread-safe for concurrent access
Internal Implementation #
HeapCharBuffer #
class HeapCharBuffer extends CharBuffer {
final char[] hb; // The underlying char array
final int offset; // The offset into the array
HeapCharBuffer(int cap, int lim) {
super(-1, 0, lim, cap, null);
hb = new char[cap];
offset = 0;
}
public char get() {
return hb[ix(nextGetIndex())];
}
public CharBuffer put(char x) {
hb[ix(nextPutIndex())] = x;
return this;
}
public char charAt(int index) {
return hb[ix(checkIndex(index))];
}
public CharSequence subSequence(int start, int end) {
int pos = position();
return new HeapCharBuffer(hb,
-1,
pos + start,
pos + end,
capacity(),
offset);
}
int ix(int i) {
return i + offset;
}
}
StringCharBuffer #
// Read-only CharBuffer backed by String
class StringCharBuffer extends CharBuffer {
private final String str;
StringCharBuffer(String str, int start, int end) {
super(-1, start, end, str.length(), null);
this.str = str;
}
public char get() {
return str.charAt(nextGetIndex());
}
public CharBuffer put(char c) {
throw new ReadOnlyBufferException();
}
public char charAt(int index) {
return str.charAt(index);
}
public CharSequence subSequence(int start, int end) {
return new StringCharBuffer(str, start, end);
}
public String toString() {
return str.substring(position(), limit());
}
}
DirectCharBuffer (via ByteBuffer view) #
// CharBuffer view of direct ByteBuffer
class ByteBufferAsCharBufferB extends CharBuffer {
private final ByteBuffer bb;
private final int offset;
ByteBufferAsCharBufferB(ByteBuffer bb) {
super(-1, 0, bb.remaining() >> 1, bb.remaining() >> 1);
this.bb = bb.duplicate().order(ByteOrder.BIG_ENDIAN);
this.offset = bb.position();
}
public char get() {
return bb.getChar(ix(nextGetIndex()));
}
public CharBuffer put(char x) {
bb.putChar(ix(nextPutIndex()), x);
return this;
}
int ix(int i) {
return (i << 1) + offset;
}
}
Performance Optimization #
Buffer Pooling for Text Processing #
public class CharBufferPool {
private final BlockingQueue<CharBuffer> pool;
public CharBufferPool(int size, int bufferSize) {
pool = new ArrayBlockingQueue<>(size);
for (int i = 0; i < size; i++) {
pool.add(CharBuffer.allocate(bufferSize));
}
}
public CharBuffer acquire() throws InterruptedException {
CharBuffer buffer = pool.take();
buffer.clear();
return buffer;
}
public void release(CharBuffer buffer) {
buffer.clear();
pool.put(buffer);
}
}
Zero-Copy Text Extraction #
// Extract substrings without copying
CharBuffer buffer = CharBuffer.allocate(1024);
buffer.put("The quick brown fox jumps over the lazy dog");
buffer.flip();
// Zero-copy substring using slice()
int wordStart = 4; // "quick"
int wordEnd = 9; // "brown"
buffer.position(wordStart).limit(wordEnd);
CharBuffer word = buffer.slice(); // No data copying
buffer.clear();
// word contains "brown" (shared backing array)
Efficient Line Reading #
public class LineReader {
private CharBuffer buffer = CharBuffer.allocate(8192);
private StringBuilder lineBuilder = new StringBuilder();
public String readLine(ReadableByteChannel channel, Charset charset)
throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(2048);
CharsetDecoder decoder = charset.newDecoder();
while (true) {
// Read bytes
int bytesRead = channel.read(byteBuffer);
if (bytesRead == -1) {
return lineBuilder.length() > 0 ? lineBuilder.toString() : null;
}
byteBuffer.flip();
// Decode to characters
decoder.decode(byteBuffer, buffer, bytesRead == -1);
buffer.flip();
// Look for newline
while (buffer.hasRemaining()) {
char ch = buffer.get();
if (ch == '\n') {
String line = lineBuilder.toString();
lineBuilder.setLength(0);
buffer.compact();
byteBuffer.compact();
return line;
} else if (ch != '\r') {
lineBuilder.append(ch);
}
}
buffer.compact();
byteBuffer.compact();
}
}
}
Related Classes #
- ByteBuffer: Byte buffer with character view support
- Charset: Character encoding specification
- CharsetEncoder: Character to byte encoding
- CharsetDecoder: Byte to character decoding
- StringCharBuffer: Read-only CharBuffer backed by String