这个 for
循环是典型的 双指针(滑动窗口)初始化结构,常用于遍历集合的连续子数组 / 子列表。以下是逐行拆解和常见场景说明:
一、循环结构解析
for (int start = 0, end = 1; // 初始化:左指针=0,右指针=1(窗口初始长度1)
end <= logEntities.size(); // 终止条件:右指针不超过集合长度
end++) { // 步进:右指针每次右移1位
// 循环体:处理 [start, end-1] 的子列表
}
关键细节:
- 窗口范围:实际处理的是 左闭右开区间
[start, end)
,即子列表为logEntities.subList(start, end)
- 子列表长度:从
1
(end=1
)到logEntities.size()
(end=size
时,子列表长度为size - 0 = size
) - 终止条件:
end <= size
而非常见的< size
,目的是包含整个集合作为最后一个子列表(当end = size
时,子列表为[0, size)
,即全部元素)
二、典型使用场景
场景 1:遍历所有以 start
开头的子数组
// 固定 start=0,遍历所有从索引0开始的子数组:
// [0], [0,1], [0,1,2], ..., [0,1,2,...,n-1]
for (int start = 0, end = 1; end <= logEntities.size(); end++) {
List<LogEntity> subList = logEntities.subList(start, end);
process(subList); // 处理从0开始,长度1~n的子列表
}
场景 2:滑动窗口(配合 start
移动)
for (int start = 0, end = 1; end <= logEntities.size(); end++) {
while (窗口条件不满足) { // 例如:子列表和超过阈值
start++; // 移动左指针缩小窗口
}
// 此时 [start, end) 是符合条件的窗口
recordWindow(start, end);
}
三、与常见循环的对比
循环写法 | 子列表范围 | 子列表数量 | 典型用途 |
---|---|---|---|
for (int i=0; i<n; i++) | 单个元素 [i] | n | 遍历元素 |
for (int i=0; i<n; i++)<br>for (int j=i+1; j<=n; j++) | [i,j) | n(n+1)/2 | 遍历所有子数组(暴力法) |
当前循环 | [0,1), [0,2), ... | n | 固定起点的子数组遍历 |
四、实际代码示例(计算前缀和)
假设 logEntities
是整数列表,计算从起点开始的所有子数组和:
List<Integer> logEntities = Arrays.asList(1, 2, 3);
int prefixSum = 0;
for (int start = 0, end = 1; end <= logEntities.size(); end++) {
prefixSum += logEntities.get(end - 1); // 累加当前元素(end-1是当前右边界)
System.out.printf("子数组 [0,%d) 的和:%d%n", end, prefixSum);
}
// 输出:
// 子数组 [0,1) 的和:1
// 子数组 [0,2) 的和:3
// 子数组 [0,3) 的和:6
五、注意事项
- 集合为空的处理:若
logEntities.size() == 0
,则end=1 > 0
,循环不会执行,避免subList(0,1)
抛异常。 start
的灵活性:若需要动态调整start
(如滑动窗口),需在循环体内添加start++
逻辑。- 索引越界风险:访问元素时需使用
end-1
(如logEntities.get(end-1)
),避免end == size
时越界。
总结
这个循环的核心作用是:以 start
为固定起点,逐步扩展右边界 end
,遍历所有从 start
开始的连续子列表(长度 1 到 n)。常见于前缀和计算、固定起点的子数组分析等场景。若在循环体内添加 start
的动态调整逻辑,则可升级为滑动窗口算法。