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

LongBuffer

8 mins

The LongBuffer class is a specialized buffer for 64-bit long integer values, providing efficient storage and manipulation of long data. It’s essential for processing large numerical datasets, timestamps, and 64-bit native data structures.

Source Code #

View Source on GitHub

Core Implementation #

public abstract class LongBuffer extends Buffer implements Comparable<LongBuffer> {
    // Factory methods
    public static LongBuffer allocate(int capacity) {
        return new HeapLongBuffer(capacity, capacity);
    }
    
    public static LongBuffer wrap(long[] array) {
        return new HeapLongBuffer(array, 0, array.length);
    }
    
    public static LongBuffer wrap(long[] array, int offset, int length) {
        return new HeapLongBuffer(array, offset, length);
    }
    
    // Long operations
    public abstract long get();
    public abstract long get(int index);
    public abstract LongBuffer put(long l);
    public abstract LongBuffer put(int index, long l);
    
    // Bulk operations
    public LongBuffer get(long[] dst) {
        return get(dst, 0, dst.length);
    }
    
    public LongBuffer get(long[] 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 LongBuffer put(long[] src) {
        return put(src, 0, src.length);
    }
    
    public LongBuffer put(long[] 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 LongBuffer duplicate();
    public abstract LongBuffer slice();
    public abstract LongBuffer slice(int index, int length);
    
    // Compact operation
    public abstract LongBuffer compact();
}

Implementation Details #

64-bit Integer Data Processing #

LongBuffer provides efficient 64-bit integer operations:

// Creating and populating LongBuffer
LongBuffer buffer = LongBuffer.allocate(100);
buffer.put(42L);
buffer.put(10000000000L);
buffer.put(Long.MAX_VALUE);
buffer.flip();

// Reading long integers
long first = buffer.get();      // 42L
long second = buffer.get();     // 10000000000L
long third = buffer.get(2);     // Long.MAX_VALUE (absolute get)

// Bulk operations with long arrays
long[] data = {1L, 2L, 3L, 4L, 5L};
LongBuffer wrapped = LongBuffer.wrap(data);  // Shares array
long[] copy = new long[5];
buffer.get(copy);              // Bulk copy

View from ByteBuffer #

LongBuffer can be created as a view of ByteBuffer (8 bytes per long):

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

// Create LongBuffer view (8 bytes per long)
LongBuffer longBuffer = byteBuffer.asLongBuffer();

// The view shares underlying byte buffer
// Writing to longBuffer affects byteBuffer
longBuffer.put(0, 0x1234567890ABCDEFL);  // Writes 8 bytes
longBuffer.put(1, 0xFEDCBA0987654321L);  // Writes next 8 bytes

// Byte order critically important for 64-bit values
byteBuffer.order(ByteOrder.BIG_ENDIAN);
LongBuffer bigEndianView = byteBuffer.asLongBuffer();
// Same bytes, completely different interpretation

Compact Operation for Long Data #

public LongBuffer compact() {
    int pos = position();
    int lim = limit();
    int rem = (pos <= lim ? lim - pos : 0);
    
    // Copy remaining longs to beginning
    for (int i = 0; i < rem; i++) {
        put(i, get(pos + i));
    }
    
    position(rem);
    limit(capacity());
    discardMark();
    return this;
}

// Usage pattern for long stream processing
LongBuffer buffer = LongBuffer.allocate(1024);
while (hasMoreData()) {
    // Fill buffer with longs
    while (buffer.hasRemaining() && hasMoreData()) {
        buffer.put(nextLong());
    }
    
    buffer.flip();
    
    // Process complete batches
    while (buffer.remaining() >= BATCH_SIZE) {
        processBatch(buffer);
    }
    
    buffer.compact();  // Move remaining data to beginning
}

Performance Characteristics #

OperationHeapLongBufferDirectLongBuffer (via ByteBuffer)
AllocationO(1) fastO(n) slower (native allocation)
Long AccessO(1) direct array accessO(1) with bounds checking
Bulk OperationsFast (System.arraycopy)Slower (element-by-element)
Memory Usage8 bytes per long + overhead8 bytes per long + native overhead
Cache LocalityGood (heap memory)Variable (depends on allocation)

Common Patterns #

Timestamp Processing #

// Process streams of timestamp data
LongBuffer processTimestamps(long[] timestamps) {
    LongBuffer buffer = LongBuffer.wrap(timestamps);
    
    // Calculate time differences
    LongBuffer differences = LongBuffer.allocate(timestamps.length - 1);
    for (int i = 1; i < buffer.limit(); i++) {
        long diff = buffer.get(i) - buffer.get(i - 1);
        differences.put(diff);
    }
    differences.flip();
    
    return differences;
}

// Filter timestamps within range
long[] filterTimestamps(long[] timestamps, long start, long end) {
    LongBuffer buffer = LongBuffer.wrap(timestamps);
    LongBuffer filtered = LongBuffer.allocate(timestamps.length);
    
    for (int i = 0; i < buffer.limit(); i++) {
        long ts = buffer.get(i);
        if (ts >= start && ts <= end) {
            filtered.put(ts);
        }
    }
    
    filtered.flip();
    long[] result = new long[filtered.remaining()];
    filtered.get(result);
    return result;
}

Large Numerical Dataset Processing #

// Process large datasets of 64-bit values
class LongDatasetProcessor {
    private LongBuffer buffer;
    private long sum = 0;
    private long count = 0;
    
    public LongDatasetProcessor(int bufferSize) {
        buffer = LongBuffer.allocate(bufferSize);
    }
    
    public void processChunk(long[] chunk) {
        buffer.put(chunk);
        buffer.flip();
        
        // Process chunk
        while (buffer.hasRemaining()) {
            long value = buffer.get();
            sum += value;
            count++;
            // Additional processing...
        }
        
        buffer.clear();
    }
    
    public double average() {
        return (double) sum / count;
    }
}

64-bit Bitmask Operations #

// Process 64-bit bitmasks
LongBuffer processBitmasks(LongBuffer masks) {
    LongBuffer result = LongBuffer.allocate(masks.remaining());
    
    while (masks.hasRemaining()) {
        long mask = masks.get();
        
        // Common bit operations
        long cleared = mask & ~0xFFL;           // Clear lower byte
        long shifted = mask << 8;               // Shift left
        long swapped = Long.reverseBytes(mask); // Byte swap
        
        result.put(swapped);
    }
    
    result.flip();
    return result;
}

// Count set bits in long values
int countSetBits(LongBuffer buffer) {
    int total = 0;
    for (int i = 0; i < buffer.limit(); i++) {
        total += Long.bitCount(buffer.get(i));
    }
    return total;
}

Native Memory Integration #

// Prepare 64-bit data for native library
LongBuffer prepareNativeLongData(long[] data) {
    // Use direct buffer for native access
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 8);
    byteBuffer.order(ByteOrder.nativeOrder());
    LongBuffer longBuffer = byteBuffer.asLongBuffer();
    
    // Copy data to direct buffer
    longBuffer.put(data);
    longBuffer.flip();
    
    return longBuffer;
}

// Native function expecting pointer to 64-bit integers
native void processLongs(LongBuffer buffer, int count);

// Usage
LongBuffer nativeBuffer = prepareNativeLongData(data);
processLongs(nativeBuffer, data.length);

Best Practices #

  1. Memory Considerations:

    • Long values consume 8 bytes each - size buffers appropriately
    • For large datasets, consider memory-mapped files
    • Monitor native memory usage with direct buffers
  2. Byte Order Sensitivity:

    // 64-bit values are highly sensitive to byte order
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // x86 native order
    LongBuffer longBuffer = byteBuffer.asLongBuffer();
    
    // Always specify byte order explicitly
    
  3. Bulk Operations for Performance:

    // Use bulk operations for better performance
    long[] batch = new long[100];
    buffer.get(batch);  // Single call vs 100 individual gets
    
    // For maximum performance with heap buffers:
    long[] data = new long[1000];
    LongBuffer buffer = LongBuffer.wrap(data);  // Zero overhead
    
  4. 64-bit Arithmetic Considerations:

    // Be careful with 64-bit arithmetic
    LongBuffer buffer = LongBuffer.allocate(100);
    
    // Check for overflow in calculations
    long a = buffer.get();
    long b = buffer.get();
    long sum;
    if (b > 0 ? a > Long.MAX_VALUE - b : a < Long.MIN_VALUE - b) {
        throw new ArithmeticException("Long overflow");
    }
    sum = a + b;
    
  5. Timestamp-Specific Handling:

    // For timestamp data, consider time units
    LongBuffer timestamps = LongBuffer.allocate(1000);
    
    // Convert between units
    long milliseconds = timestamps.get();
    long nanoseconds = milliseconds * 1_000_000L;
    
    // Handle epoch conversions
    Instant instant = Instant.ofEpochMilli(milliseconds);
    

Common Pitfalls #

  1. Byte Order Catastrophe: Wrong byte order can completely corrupt 64-bit values
  2. Memory Consumption: Long buffers consume 8× memory of equivalent byte buffers
  3. Arithmetic Overflow: 64-bit arithmetic can still overflow in calculations
  4. Platform Differences: Long size is always 64-bit in Java, but native APIs may differ
  5. Signed vs Unsigned: Java longs are signed; handle unsigned 64-bit values carefully
  6. Buffer Position Errors: Forgetting to flip() between write/read modes
  7. Native Memory Leaks: Direct buffers require careful memory management

Internal Implementation #

HeapLongBuffer #

class HeapLongBuffer extends LongBuffer {
    final long[] hb;       // The underlying long array
    final int offset;      // The offset into the array
    
    HeapLongBuffer(int cap, int lim) {
        super(-1, 0, lim, cap, null);
        hb = new long[cap];
        offset = 0;
    }
    
    HeapLongBuffer(long[] buf, int off, int len) {
        super(-1, off, off + len, buf.length, null);
        hb = buf;
        offset = 0;
    }
    
    public long get() {
        return hb[ix(nextGetIndex())];
    }
    
    public LongBuffer put(long x) {
        hb[ix(nextPutIndex())] = x;
        return this;
    }
    
    int ix(int i) {
        return i + offset;
    }
}

DirectLongBuffer (via ByteBuffer view) #

// LongBuffer view of direct ByteBuffer
class ByteBufferAsLongBufferB extends LongBuffer {
    private final ByteBuffer bb;
    private final int offset;
    
    ByteBufferAsLongBufferB(ByteBuffer bb) {
        super(-1, 0, bb.remaining() >> 3, bb.remaining() >> 3);
        this.bb = bb.duplicate().order(ByteOrder.BIG_ENDIAN);
        this.offset = bb.position();
    }
    
    public long get() {
        return bb.getLong(ix(nextGetIndex()));
    }
    
    public LongBuffer put(long x) {
        bb.putLong(ix(nextPutIndex()), x);
        return this;
    }
    
    int ix(int i) {
        return (i << 3) + offset;
    }
}

Performance Optimization #

64-bit Data Streaming #

public class LongStreamProcessor {
    private LongBuffer buffer;
    private final int batchSize;
    
    public LongStreamProcessor(int bufferSize, int batchSize) {
        this.buffer = LongBuffer.allocate(bufferSize);
        this.batchSize = batchSize;
    }
    
    public void processStream(LongSupplier supplier) {
        while (true) {
            // Fill buffer
            while (buffer.hasRemaining()) {
                long value = supplier.getAsLong();
                if (value == -1L) {  // End of stream sentinel
                    flush();
                    return;
                }
                buffer.put(value);
            }
            
            // Process full buffer
            buffer.flip();
            processBatch(buffer);
            buffer.clear();
        }
    }
    
    private void processBatch(LongBuffer batch) {
        // Optimized batch processing
        long[] temp = new long[batchSize];
        while (batch.remaining() >= batchSize) {
            batch.get(temp);
            // Process temp array...
        }
        
        // Handle remaining
        if (batch.hasRemaining()) {
            long[] remaining = new long[batch.remaining()];
            batch.get(remaining);
            // Process remaining...
        }
    }
}

Memory-Mapped Long File Processing #

// Process large long integer file with memory mapping
public void processLongFile(Path filePath) throws IOException {
    try (FileChannel channel = FileChannel.open(filePath, StandardOpenOption.READ)) {
        long fileSize = channel.size();
        
        // Ensure file size is multiple of 8 bytes
        if (fileSize % 8 != 0) {
            throw new IOException("File size not aligned to 8 bytes");
        }
        
        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);
            LongBuffer longBuffer = mappedBuffer.asLongBuffer();
            
            // Process long buffer
            processLongBuffer(longBuffer);
            
            position += size;
        }
    }
}

Long Buffer Pool for High-Throughput #

public class LongBufferPool {
    private final ConcurrentMap<Integer, Queue<LongBuffer>> pools = 
        new ConcurrentHashMap<>();
    
    public LongBuffer acquire(int size) {
        Queue<LongBuffer> pool = pools.computeIfAbsent(size, 
            k -> new ConcurrentLinkedQueue<>());
        
        LongBuffer buffer = pool.poll();
        if (buffer == null) {
            buffer = LongBuffer.allocate(size);
        }
        buffer.clear();
        return buffer;
    }
    
    public void release(LongBuffer buffer) {
        buffer.clear();
        Queue<LongBuffer> pool = pools.get(buffer.capacity());
        if (pool != null) {
            pool.offer(buffer);
        }
    }
}