Java ArrayList & Go Slice 扩容

Java 中的 ArrayList 类似 Golang 里的 Slice,都是动态数组 ArrayList 什么是 ArrayList ArrayList 是 Java 中最常见的动态数组之一,他实现了 List 接口,底层基于数组实现,但能自动扩容,可以理解为变长数组的一种。 List<String> list = new ArrayList<>(); list.add("zhangsan"); list.add("lisi"); System.out.println(list); ArrayList 的扩容原理 当创建一个 ArrayList 的时候,默认初始的容量为 10,其内部实现类似 Object[] elementData = new Object[10] 当元素超过容量时,arraylist 就会触发扩容机制,其触发条件为 if(size == elementData.length) 扩容策略如下 newCapacity = oldCapacity + (oldCapacity + 1),简言之就是扩容为原来的 1.5 倍。 这里涉及到扩容过程的主要函数是 grow() private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity + 1); if(newCapacity - minCapacity < 0) { newCapacity = minCapacity; } // 创建新数组,复制旧数据 elementData = Arrays.copyOf(elementData, newCapacity) } 这里的扩容规则实际上并没有 Go 的复杂,但是要理解 minCapacity ...

September 3, 2025 · 小石堆

浅谈 IO 多路复用

之前面试准备过这里的八股,但觉得理解不够深,故打算再探深些 五种 IO 模型 阻塞 IO 非阻塞 IO IO 多路复用 信号驱动 IO 异步 IO 阻塞 IO 最简单,最易懂 厨子做菜,做不好服务生也等着,等到菜做好再上菜。 不难看出,效率很低,但是实现简单,对应到计算机世界中,这里用 C 语言的 read() 函数去举例(虽然 C 语言烂的一坨:) 用户态调用 read(fd, buf, count) 软中断切入内核态,内核根据 fd 查文件描述符表,找到对应的内核文件描述符 检查页缓存,是不是存在数据(之前用过,缓存还在) 缓存没有,触发磁盘 IO,等待数据,进程阻塞挂起 DMA 数据 Copy 完成,触发硬件中断,进而内核把页缓存的数据 copy_to_user 到用户空间 buf 上面就是一次完整的阻塞 IO,我们可以看出,在用户态触发 read() 后,后面的逻辑基本上都是交由内核态进程去接管的,但是该用户态进程此时卡死在这里,如果 fd 对应的是网络请求,那一个 fd 就得拉一个进程接管,假设上万个请求,那系统中会有上万个阻塞进程,所以不适合并发规模较大的场景。 非阻塞IO 厨子做菜,服务生不断轮询反问厨子有没有做好。 这一点对应到 CPU 就是进程不断调用 read(),如果数据准备好了,read()出数据,如果数据没有准备好,read()出 fasle,得到 false 的进程就去继续调用 read()。 while true: data = read(fd) // 非阻塞调用 if data == EAGAIN or data == EWOULDBLOCK: // 没有数据,立刻返回错误码 do_other_work() // 去做其他事情 else: process(data) // 一旦有数据就处理 break 也就是说,用户线程需要不断询问内核数据是否准备好,虽然能及时获取到数据,但是在没有获取到数据的时候,不会主动交出 CPU 资源,而一直占用 CPU。 ...

August 11, 2025 · 小石堆