[#3623] CompositeByteBuf.iterator() should return optimized Iterable

Motivation:

CompositeByteBuf.iterator() currently creates a new ArrayList and fill it with the ByteBufs, which is more expensive then it needs to be.

Modifications:

- Use special Iterator implementation

Result:

Less overhead when calling iterator()
This commit is contained in:
Norman Maurer 2015-04-10 09:43:06 +02:00
parent 52878880b4
commit 43d57ddd46
2 changed files with 109 additions and 4 deletions

View File

@ -28,9 +28,11 @@ import java.nio.channels.ScatteringByteChannel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.NoSuchElementException;
/** /**
* A virtual buffer which shows multiple buffers as a single merged buffer. It is recommended to use * A virtual buffer which shows multiple buffers as a single merged buffer. It is recommended to use
@ -40,6 +42,7 @@ import java.util.ListIterator;
public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable<ByteBuf> { public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable<ByteBuf> {
private static final ByteBuffer EMPTY_NIO_BUFFER = Unpooled.EMPTY_BUFFER.nioBuffer(); private static final ByteBuffer EMPTY_NIO_BUFFER = Unpooled.EMPTY_BUFFER.nioBuffer();
private static final Iterator<ByteBuf> EMPTY_ITERATOR = Collections.<ByteBuf>emptyList().iterator();
private final ResourceLeak leak; private final ResourceLeak leak;
private final ByteBufAllocator alloc; private final ByteBufAllocator alloc;
@ -374,11 +377,10 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements
@Override @Override
public Iterator<ByteBuf> iterator() { public Iterator<ByteBuf> iterator() {
ensureAccessible(); ensureAccessible();
List<ByteBuf> list = new ArrayList<ByteBuf>(components.size()); if (components.isEmpty()) {
for (Component c: components) { return EMPTY_ITERATOR;
list.add(c.buf);
} }
return list.iterator(); return new CompositeByteBufIterator();
} }
/** /**
@ -1648,4 +1650,34 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements
public ByteBuf unwrap() { public ByteBuf unwrap() {
return null; return null;
} }
private final class CompositeByteBufIterator implements Iterator<ByteBuf> {
private final int size = components.size();
private int index;
@Override
public boolean hasNext() {
return size > index;
}
@Override
public ByteBuf next() {
if (size != components.size()) {
throw new ConcurrentModificationException();
}
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
return components.get(index++).buf;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read-Only");
}
}
} }

View File

@ -21,7 +21,10 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import static io.netty.buffer.Unpooled.*; import static io.netty.buffer.Unpooled.*;
import static io.netty.util.ReferenceCountUtil.*; import static io.netty.util.ReferenceCountUtil.*;
@ -865,4 +868,74 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
assertNotSame(EMPTY_BUFFER, cbuf.internalComponentAtOffset(1)); assertNotSame(EMPTY_BUFFER, cbuf.internalComponentAtOffset(1));
cbuf.release(); cbuf.release();
} }
@Test
public void testIterator() {
CompositeByteBuf cbuf = compositeBuffer();
cbuf.addComponent(EMPTY_BUFFER);
cbuf.addComponent(EMPTY_BUFFER);
Iterator<ByteBuf> it = cbuf.iterator();
assertTrue(it.hasNext());
assertSame(EMPTY_BUFFER, it.next());
assertTrue(it.hasNext());
assertSame(EMPTY_BUFFER, it.next());
assertFalse(it.hasNext());
try {
it.next();
fail();
} catch (NoSuchElementException e) {
//Expected
}
cbuf.release();
}
@Test
public void testEmptyIterator() {
CompositeByteBuf cbuf = compositeBuffer();
Iterator<ByteBuf> it = cbuf.iterator();
assertFalse(it.hasNext());
try {
it.next();
fail();
} catch (NoSuchElementException e) {
//Expected
}
cbuf.release();
}
@Test(expected = ConcurrentModificationException.class)
public void testIteratorConcurrentModificationAdd() {
CompositeByteBuf cbuf = compositeBuffer();
cbuf.addComponent(EMPTY_BUFFER);
Iterator<ByteBuf> it = cbuf.iterator();
cbuf.addComponent(EMPTY_BUFFER);
assertTrue(it.hasNext());
try {
it.next();
} finally {
cbuf.release();
}
}
@Test(expected = ConcurrentModificationException.class)
public void testIteratorConcurrentModificationRemove() {
CompositeByteBuf cbuf = compositeBuffer();
cbuf.addComponent(EMPTY_BUFFER);
Iterator<ByteBuf> it = cbuf.iterator();
cbuf.removeComponent(0);
assertTrue(it.hasNext());
try {
it.next();
} finally {
cbuf.release();
}
}
} }