What is the difference between relative and absolute get/put on a Buffer, and how do they interact with position and limit?
answer
- Relative = uses + advances position, bounded by limit
- Absolute = explicit index, ignores position
- Relative overrun: Buffer*Exception; absolute: IndexOutOfBounds
- Absolute put(index,v) to backfill a length prefix
- Absolute does not touch mark or position
basics
~20 sRelative 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 sRelative 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
Recognizes get()/put() advance through the buffer and that an indexed version exists; may not know the position/exception differences.
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.
Applies the mix idiomatically (reserve-then-patch length prefixes, peeking), and reasons about which bounds and exceptions apply in each case.
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