skip to content

What is the difference between clear() and compact() on a ByteBuffer, and when would you choose compact()?

level: middleimportance: must knowfreq 62%

answer

  1. clear(): empties logically, position=0, limit=capacity
  2. compact(): keeps unread bytes, slides them to front
  3. compact() for partial drains (keep the tail)
  4. clear() loses unread data
  5. compact() then flip() before next read

basics

~20 s

clear() resets the buffer to write mode as if empty (position=0, limit=capacity) but does not erase data — so any unread bytes are lost. compact() keeps unread bytes, moves them to the front, and positions the cursor right after them so you can append more. Use compact() when you have only partially drained the buffer.

solid answer

~50 s

Both prepare a buffer for more writing, but they treat unread data differently. clear() does position=0, limit=capacity, mark=-1 — it logically empties the buffer; the old bytes are still physically there but will be overwritten, and any data you had not yet read is abandoned. compact() instead copies the bytes between position and limit down to the start of the buffer, sets position to the number of those copied bytes, and sets limit=capacity. So unread data is preserved at the front and the next put() appends after it. You use compact() in a partial-drain loop: read from a channel, flip, consume only some bytes (e.g. up to a message boundary), then compact() so the leftover tail is retained and the next channel read fills behind it. Use clear() only when you have fully consumed the buffer (or genuinely want to discard it). Choosing clear() when bytes remain unread silently loses data.

go deeper

for a junior

Knows clear() prepares the buffer for writing again and does not erase data; may not yet distinguish the data-loss risk of clear() vs compact().

for a middle

Clearly contrasts clear() (discards unread, position=0/limit=capacity) with compact() (preserves unread, slides to front, position=remaining) and picks compact() for partial drains.

for a senior

Writes a correct read/flip/process/compact loop for framed protocols, knows compact() has a copy cost, and remembers to flip() after compact().

for a principal

Reasons about buffer reuse strategy across high-throughput pipelines: when compaction churn matters, alternatives (ring buffers, scatter/gather, sized buffers), and the data-corruption class of bugs from misusing clear().

## Setup: the three markers again Every `Buffer` maintains `position` (next index to read/write), `limit` (boundary of the active region), and `capacity` (fixed total size), with `0 <= position <= limit <= capacity`. **Write mode** means `position` points at the next free slot and `limit == capacity`. **Read mode** (entered via `flip()`) means `position` points at the next byte to read and `limit` marks the end of valid data. Both `clear()` and `compact()` return the buffer to **write mode**, but they differ in what happens to the bytes you have not yet read. ## clear() ``` position = 0; limit = capacity; mark = -1; ``` That's all it does. The word "clear" is misleading: **no bytes are erased**. It simply declares the whole buffer available for writing again. If you had read everything, that is fine. But if there were still unread bytes (between the old position and old limit), they are now logically gone — the next `put()` will start at index 0 and overwrite them. So `clear()` is correct only when the buffer has been **fully drained**. ## compact() `compact()` is the data-preserving alternative: ``` // copy unread region [position, limit) to the front System.arraycopy-equivalent of bytes [position, limit) -> [0, remaining) position = limit - oldPosition; // i.e. number of bytes moved (remaining()) limit = capacity; mark = -1; ``` The unread bytes (`remaining()` of them) are shifted to the start, `position` is left just after them, and `limit` is restored to `capacity`. The buffer is now in write mode **with the leftover data preserved at the front**, ready for new data to be appended behind it. ## Why partial drains need compact() Real I/O rarely arrives in tidy units. Suppose you read network bytes into a buffer and parse newline-delimited messages. After `flip()`, you consume complete lines, but the buffer may end with **half a line** — bytes you must keep until the rest arrives. If you call `clear()`, you throw that half-line away and corrupt the stream. `compact()` slides the half-line to the front and leaves room for the next channel read to complete it: ``` buf.clear(); while (channel.read(buf) != -1) { buf.flip(); while (hasCompleteMessage(buf)) { process(readOneMessage(buf)); } buf.compact(); // keep the partial tail, then loop to read more } ``` ## Mental model - **clear()** = "I'm done with everything in here; reuse the whole buffer." (Discards unread data.) - **compact()** = "Keep what I haven't read, make room for more." (Preserves unread data.) ## Caveats - `compact()` does a memory copy, so it is slightly more expensive than `clear()`; in a fully-drained loop prefer `clear()`. - After `compact()` you are in write mode, so you must `flip()` again before the next read. - Like `clear()`, `compact()` does not zero the rest of the buffer; old bytes beyond the moved region remain but are outside the active region.

  • After compact(), where is position and what is limit?
    position is set to remaining() (the number of unread bytes moved to the front) and limit is set to capacity — i.e. write mode with the leftover data preserved at the start, ready for new puts to append behind it.
  • Does clear() actually erase the data in the buffer?
    No. clear() only resets position to 0 and limit to capacity. The bytes remain in memory but are outside the active region and will be overwritten by subsequent writes.

saying these in an interview costs you the question

  • Believing clear() zeroes/erases the bytes (it only moves markers)
  • Using clear() in a partial-drain loop and losing the leftover tail
  • Forgetting that after compact() you are in write mode and must flip() before reading
  • Thinking compact() is free — it copies the remaining bytes

context