IntBuffer
7 mins
The IntBuffer class is a specialized buffer for integer values, providing efficient storage and manipulation of integer data. It’s commonly used for binary data processing, numerical computations, and interfacing with native libraries.
Source Code #
Core Implementation #
public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer> {
// Factory methods
public static IntBuffer allocate(int capacity) {
return new HeapIntBuffer(capacity, capacity);
}
public static IntBuffer wrap(int[] array) {
return new HeapIntBuffer(array, 0, array.length);
}
public static IntBuffer wrap(int[] array, int offset, int length) {
return new HeapIntBuffer(array, offset, length);
}
// Integer operations
public abstract int get();
public abstract int get(int index);
public abstract IntBuffer put(int i);
public abstract IntBuffer put(int index, int i);
// Bulk operations
public IntBuffer get(int[] dst) {
return get(dst, 0, dst.length);
}
public IntBuffer get(int[] 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;
}
public IntBuffer put(int[] src) {
return put(src, 0, src.length);
}
public IntBuffer put(int[] src, int offset, int length) {
checkBounds(offset, length, src.length);
if (length > remaining())
throw new BufferOverflowException();
for (int i = offset; i < offset + length; i++)
put(src[i]);
return this;
}
// View operations
public abstract IntBuffer duplicate();
public abstract IntBuffer slice();
public abstract IntBuffer slice(int index, int length);
// Compact operation
public abstract IntBuffer compact();
}
Implementation Details #
Integer Data Processing #
IntBuffer provides efficient integer operations:
// Creating and populating IntBuffer
IntBuffer buffer = IntBuffer.allocate(100);
buffer.put(42);
buffer.put(100);
buffer.put(255);
buffer.flip();
// Reading integers
int first = buffer.get(); // 42
int second = buffer.get(); // 100
int third = buffer.get(2); // 255 (absolute get)
// Bulk operations
int[] data = {1, 2, 3, 4, 5};
IntBuffer wrapped = IntBuffer.wrap(data); // Shares array
int[] copy = new int[5];
buffer.get(copy); // Bulk copy
View from ByteBuffer #
IntBuffer can be created as a view of ByteBuffer:
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
// Create IntBuffer view (4 bytes per integer)
IntBuffer intBuffer = byteBuffer.asIntBuffer();
// The view shares underlying byte buffer
// Writing to intBuffer affects byteBuffer
intBuffer.put(0, 0x12345678); // Writes 4 bytes
intBuffer.put(1, 0x9ABCDEF0); // Writes next 4 bytes
// Byte order matters!
byteBuffer.order(ByteOrder.BIG_ENDIAN);
IntBuffer bigEndianView = byteBuffer.asIntBuffer();
// Same bytes, different interpretation
Compact Operation for Integer Data #
public IntBuffer compact() {
int pos = position();
int lim = limit();
int rem = (pos <= lim ? lim - pos : 0);
// Copy remaining integers to beginning
for (int i = 0; i < rem; i++) {
put(i, get(pos + i));
}
position(rem);
limit(capacity());
discardMark();
return this;
}
// Usage pattern for integer stream processing
IntBuffer buffer = IntBuffer.allocate(1024);
while (hasMoreData()) {
// Fill buffer with integers
while (buffer.hasRemaining() && hasMoreData()) {
buffer.put(nextInt());
}
buffer.flip();
// Process complete batches
while (buffer.remaining() >= BATCH_SIZE) {
processBatch(buffer);
}
buffer.compact(); // Move remaining data to beginning
}
Performance Characteristics #
| Operation | HeapIntBuffer | DirectIntBuffer (via ByteBuffer) |
|---|---|---|
| Allocation | O(1) fast | O(n) slower (native allocation) |
| Integer Access | O(1) direct array access | O(1) with bounds checking |
| Bulk Operations | Fast (System.arraycopy) | Slower (element-by-element) |
| Memory Usage | 4 bytes per integer + overhead | 4 bytes per integer + native overhead |
| Cache Locality | Good (heap memory) | Variable (depends on allocation) |
Common Patterns #
Integer Array Processing #
// Efficient processing of integer arrays
int[] processData(int[] input) {
IntBuffer buffer = IntBuffer.wrap(input); // Zero-copy wrapper
// Process in place
for (int i = 0; i < buffer.limit(); i++) {
int value = buffer.get(i);
buffer.put(i, transform(value));
}
return input; // Original array modified
}
// Alternative with bulk operations
IntBuffer processBulk(int[] input) {
IntBuffer buffer = IntBuffer.allocate(input.length);
buffer.put(input); // Bulk copy
buffer.flip();
// Process with buffer operations
while (buffer.hasRemaining()) {
int pos = buffer.position();
int value = buffer.get();
buffer.put(pos, process(value));
}
return buffer;
}
Binary Data Interpretation #
// Interpret binary data as integers
ByteBuffer byteData = readBinaryData();
byteData.order(ByteOrder.BIG_ENDIAN);
// View as integers (4 bytes each)
IntBuffer intView = byteData.asIntBuffer();
// Process integer data
int[] header = new int[4];
intView.get(header, 0, 4); // Read first 4 integers
// Skip header and process payload
intView.position(4);
while (intView.hasRemaining()) {
int value = intView.get();
processInteger(value);
}
Numerical Computation Buffer #
class IntegerAccumulator {
private IntBuffer buffer;
private int sum = 0;
private int count = 0;
public IntegerAccumulator(int capacity) {
buffer = IntBuffer.allocate(capacity);
}
public void add(int value) {
if (!buffer.hasRemaining()) {
flush();
}
buffer.put(value);
sum += value;
count++;
}
public void flush() {
buffer.flip();
// Process batch
while (buffer.hasRemaining()) {
int value = buffer.get();
// Process value...
}
buffer.clear();
}
public double average() {
return (double) sum / count;
}
}
Interfacing with Native Libraries #
// Prepare integer data for native library
IntBuffer prepareNativeData(int[] data) {
// Use direct buffer for native access
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
IntBuffer intBuffer = byteBuffer.asIntBuffer();
// Copy data to direct buffer
intBuffer.put(data);
intBuffer.flip();
return intBuffer;
}
// Native function expecting pointer to integers
native void processIntegers(IntBuffer buffer, int count);
// Usage
IntBuffer nativeBuffer = prepareNativeData(data);
processIntegers(nativeBuffer, data.length);
Best Practices #
Choose Appropriate Buffer Type:
- Use
HeapIntBufferfor short-lived, small datasets - Use
DirectIntBuffer(via ByteBuffer) for native library integration - Consider memory-mapped files for large integer datasets
- Use
Handle Byte Order:
// Always specify byte order for multi-byte data ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // x86 native order IntBuffer intBuffer = byteBuffer.asIntBuffer();Use Bulk Operations:
// Prefer bulk get/put over single element operations int[] batch = new int[100]; buffer.get(batch); // Single call vs 100 individual gets // Or use array-backed buffer for maximum performance int[] data = new int[1000]; IntBuffer buffer = IntBuffer.wrap(data); // Zero overheadReuse Buffers:
// Buffer pool for integer processing public class IntBufferPool { private final Queue<IntBuffer> pool = new ArrayDeque<>(); public IntBuffer acquire(int size) { IntBuffer buffer = pool.poll(); if (buffer == null || buffer.capacity() < size) { return IntBuffer.allocate(size); } buffer.clear(); return buffer; } public void release(IntBuffer buffer) { buffer.clear(); pool.offer(buffer); } }Monitor Buffer Limits:
- Check
remaining()before bulk operations - Use
hasRemaining()in processing loops - Handle buffer overflow/underflow gracefully
- Check
Common Pitfalls #
- Byte Order Mismatch: Interpreting integers with wrong byte order
- Buffer Overflow: Writing beyond capacity without checking
remaining() - Position Management: Forgetting to
flip()orclear()between operations - Memory Leaks: Not releasing direct buffers (native memory)
- Thread Safety: IntBuffer is not thread-safe for concurrent access
- Array Bounds: Accessing indices beyond
limit()orcapacity() - View Consistency: Multiple views of same ByteBuffer with different byte orders
Internal Implementation #
HeapIntBuffer #
class HeapIntBuffer extends IntBuffer {
final int[] hb; // The underlying integer array
final int offset; // The offset into the array
HeapIntBuffer(int cap, int lim) {
super(-1, 0, lim, cap, null);
hb = new int[cap];
offset = 0;
}
HeapIntBuffer(int[] buf, int off, int len) {
super(-1, off, off + len, buf.length, null);
hb = buf;
offset = 0;
}
public int get() {
return hb[ix(nextGetIndex())];
}
public IntBuffer put(int x) {
hb[ix(nextPutIndex())] = x;
return this;
}
int ix(int i) {
return i + offset;
}
}
DirectIntBuffer (via ByteBuffer view) #
// IntBuffer view of direct ByteBuffer
class ByteBufferAsIntBufferB extends IntBuffer {
private final ByteBuffer bb;
private final int offset;
ByteBufferAsIntBufferB(ByteBuffer bb) {
super(-1, 0, bb.remaining() >> 2, bb.remaining() >> 2);
this.bb = bb.duplicate().order(ByteOrder.BIG_ENDIAN);
this.offset = bb.position();
}
public int get() {
return bb.getInt(ix(nextGetIndex()));
}
public IntBuffer put(int x) {
bb.putInt(ix(nextPutIndex()), x);
return this;
}
int ix(int i) {
return (i << 2) + offset;
}
}
Performance Optimization #
Integer Stream Processing #
public class IntegerStreamProcessor {
private IntBuffer buffer;
private final int batchSize;
public IntegerStreamProcessor(int bufferSize, int batchSize) {
this.buffer = IntBuffer.allocate(bufferSize);
this.batchSize = batchSize;
}
public void processStream(IntSupplier supplier) {
while (true) {
// Fill buffer
while (buffer.hasRemaining()) {
int value = supplier.getAsInt();
if (value == -1) { // End of stream
flush();
return;
}
buffer.put(value);
}
// Process full buffer
buffer.flip();
processBatch(buffer);
buffer.clear();
}
}
private void processBatch(IntBuffer batch) {
// Optimized batch processing
int[] temp = new int[batchSize];
while (batch.remaining() >= batchSize) {
batch.get(temp);
// Process temp array...
}
// Handle remaining
if (batch.hasRemaining()) {
int[] remaining = new int[batch.remaining()];
batch.get(remaining);
// Process remaining...
}
}
}
Memory-Mapped Integer File #
// Process large integer file with memory mapping
public void processIntegerFile(Path filePath) throws IOException {
try (FileChannel channel = FileChannel.open(filePath, StandardOpenOption.READ)) {
long fileSize = channel.size();
long position = 0;
while (position < fileSize) {
long size = Math.min(Integer.MAX_VALUE, fileSize - position);
MappedByteBuffer mappedBuffer = channel.map(
FileChannel.MapMode.READ_ONLY, position, size);
mappedBuffer.order(ByteOrder.BIG_ENDIAN);
IntBuffer intBuffer = mappedBuffer.asIntBuffer();
// Process integer buffer
processIntBuffer(intBuffer);
position += size;
}
}
}
Integer Buffer Pool #
public class IntBufferPool {
private final ConcurrentMap<Integer, Queue<IntBuffer>> pools =
new ConcurrentHashMap<>();
public IntBuffer acquire(int size) {
Queue<IntBuffer> pool = pools.computeIfAbsent(size,
k -> new ConcurrentLinkedQueue<>());
IntBuffer buffer = pool.poll();
if (buffer == null) {
buffer = IntBuffer.allocate(size);
}
buffer.clear();
return buffer;
}
public void release(IntBuffer buffer) {
buffer.clear();
Queue<IntBuffer> pool = pools.get(buffer.capacity());
if (pool != null) {
pool.offer(buffer);
}
}
}
Related Classes #
- ByteBuffer: Byte buffer with integer view support
- LongBuffer: Long integer buffer implementation
- FloatBuffer: Floating-point buffer implementation
- DoubleBuffer: Double-precision buffer implementation
- ShortBuffer: Short integer buffer implementation
- MappedByteBuffer: Memory-mapped file buffer