I\'m writing GC friendly code to read and return to the user a series of byte[]
messages. Internally I reuse the same ByteBuffer which means I\'ll repeatedly re
Just like Keith Randall I'd also create Iterator<ByteArray>
, but working quite differently (the annotations below come from lombok):
@RequiredArgsConstructor
public class ByteArray {
@Getter private final byte[] data;
private final ByteArrayIterable source;
void allowReuse() {
source.allowReuse();
}
}
public class ByteArrayIterable implements Iterable<ByteArray> {
private boolean allowReuse;
public allowReuse() {
allowReuse = true;
}
public Iterator<ByteArray> iterator() {
return new AbstractIterator<ByteArray>() {
private ByteArray nextElement;
public ByteArray computeNext() {
if (noMoreElements()) return endOfData();
if (!allowReuse) nextElement =
new ByteArray(new byte[length], ByteArrayIterable.this);
allowReuse = false;
fillWithNewData(lastElement.getData());
}
}
}
}
Now in calls like Lists.newArrayList(myIterator)
always a new byte array gets allocated, so everything works. In your loops like
for (ByteArray a : myByteArrayIterable) {
a.allowReuse();
process(a.getData());
}
the buffer gets reused. No harm may result, unless you call allowReuse()
by mistake. If you forget to call it, then you get worse performance but correct behavior.
Now I see it could work without ByteArray
, the important thing is that myByteArrayIterable.allowReuse()
gets called, which could be done directly.
EnumMap
did essentially exactly this in its entrySet()
iterator, which causes confusing, crazy, depressing bugs to this day.
If I were you, I just wouldn't use an Iterator
-- I'd write a different API (possibly quite dissimilar from Iterator, even) and implement that. For example, you might write a new API that takes as input the ByteBuffer
to write the message into, so users of the API could control whether or not the buffer gets reused. That seems reasonably intuitive (the user can write code that obviously and cleanly reuses the ByteBuffer
), without creating unnecessarily cluttered code.
I would define an intermediate object which you can invalidate. So your function would return an Iterator<ByteArray>
, and ByteArray
is something like this:
class ByteArray {
private byte[] data;
ByteArray(byte[] d) { data = d; }
byte[] getData() {
if (data == null) throw new BadUseOfIteratorException();
return data;
}
void invalidate() { data = null; }
}
Then your iterator can invalidate the previously returned ByteArray
so that any future access (via getData
, or any other accessor you provide) will fail. Then at least if someone does something like Lists.newArrayList(myIterator)
, they will at least get an error (when the first invalid ByteArray
is accessed) instead of silently returning the wrong data.
Of course, this won't catch all possible bad uses, but probably the common ones. If you're happy with never returning the raw byte[]
and providing accessors like byte get(int idx)
instead, then it should catch all cases.
You will have to allocate a new ByteArray
for each iterator return, but hopefully that's a lot less expensive than copying your byte[]
for each iterator return.