Skip to main content
  1. Java NIO (New I/O)/

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 #

View Source on GitHub

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 #

OperationHeapIntBufferDirectIntBuffer (via ByteBuffer)
AllocationO(1) fastO(n) slower (native allocation)
Integer AccessO(1) direct array accessO(1) with bounds checking
Bulk OperationsFast (System.arraycopy)Slower (element-by-element)
Memory Usage4 bytes per integer + overhead4 bytes per integer + native overhead
Cache LocalityGood (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 #

  1. Choose Appropriate Buffer Type:

    • Use HeapIntBuffer for short-lived, small datasets
    • Use DirectIntBuffer (via ByteBuffer) for native library integration
    • Consider memory-mapped files for large integer datasets
  2. 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();
    
  3. 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 overhead
    
  4. Reuse 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);
        }
    }
    
  5. Monitor Buffer Limits:

    • Check remaining() before bulk operations
    • Use hasRemaining() in processing loops
    • Handle buffer overflow/underflow gracefully

Common Pitfalls #

  1. Byte Order Mismatch: Interpreting integers with wrong byte order
  2. Buffer Overflow: Writing beyond capacity without checking remaining()
  3. Position Management: Forgetting to flip() or clear() between operations
  4. Memory Leaks: Not releasing direct buffers (native memory)
  5. Thread Safety: IntBuffer is not thread-safe for concurrent access
  6. Array Bounds: Accessing indices beyond limit() or capacity()
  7. 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);
        }
    }
}