在Java业务开发中,经常需要对对象列表进行条件统计,比如统计符合某个属性的对象数量,当遇到特定条件时重置计数重新开始统计。传统的多次遍历方式会增加时间复杂度,本文介绍单次遍历实现该需求的方法。

传统实现方式的问题
很多开发者处理这类需求时会采用多次遍历的方式,比如先遍历列表找到重置条件的位置,再分段统计计数,这种方式的时间复杂度是O(n*k),k是重置次数,数据量大时性能较差。还有开发者会在循环中反复判断条件,但是计数逻辑分散,代码可读性低。
单次遍历优化思路
核心思路是在一次遍历中同时维护计数变量和重置判断逻辑,遇到需要重置的条件时直接将计数归零,同时可以继续统计后续的符合条件的对象。只需要一个循环就能完成所有操作,时间复杂度降为O(n)。
基础实现示例
假设我们有一个用户对象列表,需要统计连续活跃的用户数量,当遇到不活跃的用户时重置计数,以下是实现代码:
// 用户对象定义
class User {
private String name;
private boolean isActive;
public User(String name, boolean isActive) {
this.name = name;
this.isActive = isActive;
}
public boolean getIsActive() {
return isActive;
}
}
public class CountResetDemo {
public static void main(String[] args) {
// 初始化用户列表
List<User> userList = new ArrayList<>();
userList.add(new User("张三", true));
userList.add(new User("李四", true));
userList.add(new User("王五", false));
userList.add(new User("赵六", true));
userList.add(new User("孙七", true));
userList.add(new User("周八", true));
userList.add(new User("吴九", false));
int currentCount = 0;
int maxContinuousActive = 0;
// 单次遍历实现计数和重置
for (User user : userList) {
if (user.getIsActive()) {
// 符合条件,计数加1
currentCount++;
// 更新最大连续计数
if (currentCount > maxContinuousActive) {
maxContinuousActive = currentCount;
}
} else {
// 遇到不符合条件的用户,重置计数
currentCount = 0;
}
}
System.out.println("最大连续活跃用户数:" + maxContinuousActive);
}
}
复杂条件场景实现
如果存在多个重置条件,比如除了不活跃之外,用户所属部门变更也需要重置计数,只需要扩展判断逻辑即可:
class User {
private String name;
private boolean isActive;
private String department;
public User(String name, boolean isActive, String department) {
this.name = name;
this.isActive = isActive;
this.department = department;
}
public boolean getIsActive() {
return isActive;
}
public String getDepartment() {
return department;
}
}
public class ComplexCountResetDemo {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User("张三", true, "技术部"));
userList.add(new User("李四", true, "技术部"));
userList.add(new User("王五", true, "产品部"));
userList.add(new User("赵六", true, "产品部"));
userList.add(new User("孙七", false, "产品部"));
userList.add(new User("周八", true, "产品部"));
int currentCount = 0;
int maxCount = 0;
String lastDepartment = null;
for (User user : userList) {
// 判断是否需要重置:不活跃或者部门变更
if (!user.getIsActive() || (lastDepartment != null && !lastDepartment.equals(user.getDepartment()))) {
currentCount = 0;
}
if (user.getIsActive()) {
currentCount++;
if (currentCount > maxCount) {
maxCount = currentCount;
}
}
lastDepartment = user.getDepartment();
}
System.out.println("同部门最大连续活跃用户数:" + maxCount);
}
}
优化效果对比
我们通过表格对比两种方式的时间复杂度:
| 实现方式 | 时间复杂度 | 代码可读性 | 适用场景 |
|---|---|---|---|
| 多次遍历 | O(n*k) | 中等 | 数据量小、逻辑简单 |
| 单次遍历优化 | O(n) | 高 | 数据量大、条件复杂 |
注意事项
- 重置条件的判断要放在计数逻辑之前,避免不符合条件的对象被计入
- 如果需要在重置时保留之前的计数结果,可以额外维护一个结果列表存储每次的计数
- 处理对象属性时要注意空值判断,避免空指针异常
这种单次遍历的优化方式不仅适用于计数重置场景,也可以扩展到其他需要单次循环处理多逻辑的列表操作场景,能有效提升代码执行效率,减少不必要的性能损耗。