处理数据流时,你没法预知总量,更没法全塞进内存。日志、数据库游标、网络流——这些东西像水龙头里的水,开着开着就漫出来了。Reservoir Sampling就是这时候掏出来的老工具。
算法本身很朴素:固定一个容量为k的"蓄水池",每来一个新元素,按概率决定是否替换池中的旧货。数学保证每个元素最终入池的概率完全相等,哪怕你根本不知道流有多长。这玩意儿1956年就发表了,比Java本身还老。
Java生态里藏着不少现成实现。Apache Commons Math的ReservoirSampler、Google Guava的ReservoirSample都封装好了。但有意思的是,大部分开发者面试被问到"如何在未知长度的流里均匀采样"时,还是会愣住。
一位在Substack写JVM专栏的作者最近重提了这事。他原话是:「That balance is what makes it useful for large input or data sources that do not have a known end」——说白了,这算法解决的就是"既要又要"的 classic 困境:内存有限,公平性不能丢。
实际用的时候有个细节容易踩坑:随机数生成器的质量。JDK自带的ThreadLocalRandom够用了,但要是采样本身成了瓶颈,换成SplittableRandom能省点开销。这点在GitHub上某个高星Java算法库里被标成了"常见误用"。
热门跟贴