removeComponent删除组件
前一篇讲了新增操作,接下来讲讲其他的,首先当然是删除啦,删除索引对应的缓冲区。原理就是获取对应的组件,然后释放,然后从组件数组中删除
public CompositeByteBuf removeComponent(int cIndex) {
checkComponentIndex(cIndex);
Component comp = components[cIndex];
if (lastAccessed == comp) {//查找的时候缓存用的
lastAccessed = null;
}
comp.free();//释放缓冲区
removeComp(cIndex);//删除组件
if (comp.length() > 0) {//如果有可读数据被删除了,那就需要更新之后的组件索引
updateComponentOffsets(cIndex);
}
return this;
}
//删除索引对应的组件
private void removeComp(int i) {
removeCompRange(i, i + 1);
}
getByte获取数据
增删讲完了,该说说读和写吧,获取指定索引的字节数据。
@Override
public byte getByte(int index) {
Component c = findComponent(index);
return c.buf.getByte(c.idx(index));//根据索引获取字节数据
}
findComponent查找组件
如果缓存的那个索引区域就是我们要找的,就直接返回去,否则就要去找。
private Component findComponent(int offset) {
Component la = lastAccessed;//缓存的
if (la != null && offset >= la.offset && offset < la.endOffset) {//索引在缓存组件内
ensureAccessible();
return la;
}
checkIndex(offset);
return findIt(offset);
}
findIt二分查找
二分查找法查找,找到的组件会缓存到lastAccessed
。
private Component findIt(int offset) {
for (int low = 0, high = componentCount; low <= high;) {
int mid = low + high >>> 1;
Component c = components[mid];
if (offset >= c.endOffset) {
low = mid + 1;
} else if (offset < c.offset) {
high = mid - 1;
} else {
lastAccessed = c;
return c;
}
}
throw new Error("should not reach here");
}
其实写也是类似的:
toComponentIndex获取偏移对应的组件索引
获取索引后当然就可以获取组件啦,当组件数量少的时候用了一些快速的方法,多了就用二分查找。
public int toComponentIndex(int offset) {
checkIndex(offset);
return toComponentIndex0(offset);
}
//二分查找
private int toComponentIndex0(int offset) {
int size = componentCount;
if (offset == 0) { // fast-path zero offset
for (int i = 0; i < size; i++) {
if (components[i].endOffset > 0) {
return i;//返回存在可读的第一个组件的索引
}
}
}
if (size <= 2) { // fast-path for 1 and 2 component count
return size == 1 || offset < components[0].endOffset ? 0 : 1;//个数为1,直接就是0。如果是2的话,看偏移是否小于第一个组件的endOffset,是就是0,否就是1
}
for (int low = 0, high = size; low <= high;) {//二分查找
int mid = low + high >>> 1;
Component c = components[mid];
if (offset >= c.endOffset) {
low = mid + 1;
} else if (offset < c.offset) {
high = mid - 1;
} else {
return mid;
}
}
throw new Error("should not reach here");
}
addFlattenedComponents可以添加复合缓冲区
这个方法可以判断添加的是什么类型的,如果不是复合的就按一般方法处理,如果是符合的,就把里面可读的组件都添加进来,注意如果待添加的复合缓冲区不可读的话,就不会添加,什么也不做。
public CompositeByteBuf addFlattenedComponents(boolean increaseWriterIndex, ByteBuf buffer) {
checkNotNull(buffer, "buffer");
final int ridx = buffer.readerIndex();
final int widx = buffer.writerIndex();
if (ridx == widx) {//不可读了就释放
buffer.release();
return this;
}
if (!(buffer instanceof CompositeByteBuf)) {//不是复合类型的就直接调用加入
addComponent0(increaseWriterIndex, componentCount, buffer);
consolidateIfNeeded();
return this;
}
final CompositeByteBuf from = (CompositeByteBuf) buffer;
from.checkIndex(ridx, widx - ridx);
final Component[] fromComponents = from.components;//取出所有组件
final int compCountBefore = componentCount;//增加前个数
final int writerIndexBefore = writerIndex;//增加前的写索引
try {//从读索引开始获取对应的fromComponents组件获取信息,并重新创建组件添加到最后
for (int cidx = from.toComponentIndex0(ridx), newOffset = capacity();; cidx++) {
final Component component = fromComponents[cidx];//获取索引对应的组件
final int compOffset = component.offset;//获取偏移
final int fromIdx = Math.max(ridx, compOffset);//起点索引
final int toIdx = Math.min(widx, component.endOffset);//终点索引
final int len = toIdx - fromIdx;//还有多少个可读字节
if (len > 0) { // skip empty components //不是空的,就创建组件添加到后面
addComp(componentCount, new Component(
component.srcBuf.retain(), component.srcIdx(fromIdx),
component.buf, component.idx(fromIdx), newOffset, len, null));
}
if (widx == toIdx) {//已经处理完了
break;
}
newOffset += len;//更新偏移
}
if (increaseWriterIndex) {
writerIndex = writerIndexBefore + (widx - ridx);
}
consolidateIfNeeded();//必要就合并
buffer.release();//释放
buffer = null;
return this;
} finally {
if (buffer != null) {//有异常的话需要回滚
// if we did not succeed, attempt to rollback any components that were added
if (increaseWriterIndex) {
writerIndex = writerIndexBefore;
}
for (int cidx = componentCount - 1; cidx >= compCountBefore; cidx--) {
components[cidx].free();//释放缓冲区
removeComp(cidx);//删除组件
}
}
}
}
iterator获取缓冲区迭代器
就是把里面的缓冲区都封装到迭代器里了,而且是只读的,获取的是组件里源缓冲区的切片slice
。
public Iterator<ByteBuf> iterator() {
ensureAccessible();
return componentCount == 0 ? EMPTY_ITERATOR : new CompositeByteBufIterator();
}
切片这个时候有用了,而且是源缓冲区:
isDirect是否是直接缓冲区
只有组件里的缓冲区全部是直接缓冲区的时候,才是直接缓冲区。
capacity有多少可读字节
deallocate释放缓冲区
根据组件数量来判断的方法
组件数量两个的时候可能就没结果了。
主要的方法都介绍了,只要理解这些,其他的大部分方法也都是这些简单的组合而已,就不多说了。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。