skip to content

What is the difference between relative and absolute get/put on a Buffer, and how do they interact with position and limit?

level: middleimportance: should knowfreq 45%

answer

  1. Relative = uses + advances position, bounded by limit
  2. Absolute = explicit index, ignores position
  3. Relative overrun: Buffer*Exception; absolute: IndexOutOfBounds
  4. Absolute put(index,v) to backfill a length prefix
  5. Absolute does not touch mark or position

basics

~20 s

Relative get()/put(value) operate at the current position and then advance it by one, bounded by limit. Absolute get(index)/put(index, value) take an explicit index, do not read or change position, and are bounded by capacity-ish range checks. Relative is for streaming; absolute is for random access.

solid answer

~50 s

Relative operations — get() and put(b) — use the shared position cursor: they act at position, then increment it, and they throw BufferUnderflowException/BufferOverflowException when position would cross limit. Because they consume the cursor, sequences of relative calls naturally stream through the active region, and flip()/clear()/remaining() are all designed around them. Absolute operations — get(int index) and put(int index, b) — take the index explicitly, do not consult or modify position or mark, and are bounds-checked against the buffer's valid range (throwing IndexOutOfBoundsException). They are ideal for random access, patching a length prefix after you know the body size, or peeking without disturbing the stream. A common pattern: reserve 4 bytes for a length, write the payload with relative puts, then go back with an absolute put(index, len) to fill in the count without rewinding the cursor.

go deeper

for a junior

Recognizes get()/put() advance through the buffer and that an indexed version exists; may not know the position/exception differences.

for a middle

States that relative ops use and advance position (bounded by limit) while absolute ops take an index and leave position untouched, and names the differing exceptions.

for a senior

Applies the mix idiomatically (reserve-then-patch length prefixes, peeking), and reasons about which bounds and exceptions apply in each case.

for a principal

Designs binary protocol codecs leveraging absolute access to avoid extra passes/copies, and weighs readability vs. the subtle cursor-vs-index footguns in shared buffer code.

## The two access styles A `Buffer` exposes element access in two flavors. The distinction is entirely about whether the operation uses the buffer's internal **position** cursor. ### Relative operations Methods with no index argument: `get()`, `put(byte b)` (and the typed variants like `getInt()`, `putInt(int)`). - They act at the current `position`. - They then **advance** `position` by the number of bytes consumed (1 for a byte, 4 for an int, etc.). - They are bounded by `limit`: a relative read past `limit` throws `BufferUnderflowException`; a relative write past `limit` throws `BufferOverflowException`. Because relative ops move the cursor, they are the streaming interface — you fill or drain by calling them repeatedly, and `hasRemaining()` (position < limit) tells you when to stop. The whole flip/clear/compact machinery exists to manage the cursor for relative access. ### Absolute operations Methods that take an explicit index: `get(int index)`, `put(int index, byte b)` (and typed `getInt(int index)`, etc.). - They act at the **given index**, ignoring `position` entirely. - They **do not change** `position` or `mark`. - They are bounds-checked against the buffer (an out-of-range index throws `IndexOutOfBoundsException`, not the Buffer over/underflow exceptions). For a relative-region-agnostic op, the valid range is the whole buffer, independent of limit in older docs — always treat indices as within [0, capacity) for the element width. Absolute ops are the random-access interface: peek at byte 7 without disturbing where you are, or overwrite a header field after the fact. ## Why both exist — a worked example Many binary formats start with a length prefix you don't know until you've written the body. With a mix of relative and absolute puts you avoid a second pass: ``` int lenPos = buf.position(); // remember where the length goes buf.putInt(0); // relative: reserve 4 bytes, position += 4 int bodyStart = buf.position(); writeBody(buf); // relative puts advance position int bodyLen = buf.position() - bodyStart; buf.putInt(lenPos, bodyLen); // ABSOLUTE: patch the length, position untouched ``` The absolute `putInt(lenPos, bodyLen)` backfills the prefix without rewinding the cursor, so the next relative write continues exactly where the body ended. ## Summary table | | uses position | changes position | bounds | exception on overrun | |---|---|---|---|---| | relative get()/put(v) | yes | yes (+width) | limit | BufferUnder/OverflowException | | absolute get(i)/put(i,v) | no | no | index range | IndexOutOfBoundsException | ## Practical guidance - Use **relative** for sequential filling/draining — it cooperates with flip/clear/remaining. - Use **absolute** for peeking, patching, or random access without perturbing the streaming cursor. - Mixing them is fine and idiomatic, as shown above.

  • Why is absolute put(index, value) useful when writing a length-prefixed frame?
    You can reserve the prefix with a relative put, stream the body (advancing position), then patch the now-known length with an absolute put at the reserved index — without rewinding the cursor, so subsequent writes continue after the body.
  • Which exceptions distinguish a relative overrun from an absolute one?
    Relative reads/writes past limit throw BufferUnderflowException / BufferOverflowException; an absolute index outside the valid range throws IndexOutOfBoundsException.

saying these in an interview costs you the question

  • Thinking absolute get(index) advances position
  • Expecting BufferUnderflowException from an out-of-range absolute index (it's IndexOutOfBoundsException)
  • Believing absolute ops respect limit the same way relative ones do
  • Using absolute ops in a streaming loop and wondering why position never moves

context