
本文揭示 GetChangedLedObjects() 方法中因在遍历 LinkedList 时错误调用 add() 导致无限扩容的内存溢出根源,并提供基于 Map 的简洁、安全、可维护的重构方案。
本文揭示 `getchangedledobjects()` 方法中因在遍历 linkedlist 时错误调用 `add()` 导致无限扩容的内存溢出根源,并提供基于 map 的简洁、安全、可维护的重构方案。
问题核心并非“真实内存不足”,而是典型的逻辑型内存泄漏:在 GetChangedLedObjects() 方法中,代码一边遍历 myList,一边向同一 LinkedList 实例中不断添加元素:
public LinkedList<LED> GetChangedLedObjects() {
LinkedList<LED> myList = getLedLinkedList(); // 初始含16个LED
for (int i = 0; i < myList.size(); i++) {
LED led = myList.get(i);
if (led.updateRequired == true) {
myList.add(led); // ⚠️ 危险!向正在遍历的列表添加元素
}
led = null;
}
return myList;
}由于 LinkedList.size() 动态增长,而循环条件 i 每次都重新求值,一旦存在任一 <code>updateRequired == true 的 LED(例如 zone1),循环将永远无法终止——每次 add(led) 都使 myList.size() +1,i 却只递增 1,最终触发 OutOfMemoryError。这不是 JVM 堆配置问题,而是无限循环式内存耗尽。
此外,原始实现还存在多处低效与潜在 Bug:
-
InitialiseStateMachine中重复的样板赋值(16 次几乎相同逻辑); -
zone8、tamper、sos初始化时误用zone6.Id(应为对应Button的Id); -
StateHasChanged()和GetChangedLedObjects()均反复构建新LinkedList,造成不必要的对象分配; - 手动索引遍历
LinkedList.get(i)违背链表设计初衷(get(i)时间复杂度为 O(n),整体变为 O(n²))。
✅ 推荐重构方案:使用 Map<string led></string> 替代硬编码字段 + LinkedList 构建
该方案彻底规避遍历中修改集合的风险,提升可读性、可扩展性与性能:
class StateMachine {
private final Map<String, LED> ledMap = new HashMap<>();
public void InitialiseStateMachine(Map<String, Button> buttonMap) {
// 批量初始化 zone1–zone8
for (int i = 1; i <= 8; i++) {
String key = "zone" + i;
Button btn = buttonMap.getOrDefault(key, new Button());
ledMap.put(key, new LED(btn.Id, btn.onStateDrawable, btn.offStateDrawable));
}
// 批量初始化其他 LED(利用枚举统一管理键名)
for (LED.Value v : LED.Value.values()) {
String key = v.name().toLowerCase();
Button btn = buttonMap.getOrDefault(key, new Button());
ledMap.put(key, new LED(btn.Id, btn.onStateDrawable, btn.offStateDrawable));
}
}
public boolean StateHasChanged() {
return ledMap.values().stream().anyMatch(led -> led.updateRequired);
}
public LinkedList<LED> GetChangedLedObjects() {
LinkedList<LED> result = new LinkedList<>();
for (LED led : ledMap.values()) {
if (led.updateRequired) {
result.add(led);
}
}
return result;
}
public void SetZone1Status(boolean state) {
LED led = ledMap.get("zone1");
if (led != null && state != led.onState) {
led.onState = state;
led.updateRequired = true;
}
}
}关键改进点说明:
- ✅ 安全遍历:
GetChangedLedObjects()创建全新LinkedList存储结果,原ledMap不被修改,杜绝无限循环; - ✅ O(1) 查找:通过
ledMap.get("zone1")直接获取,无需遍历或索引; - ✅ 消除重复代码:使用循环和枚举自动处理同类初始化,避免手误(如原代码中
zone8.Id = zone6.Id); - ✅ 语义清晰:
LED.Value枚举明确声明所有非 zone 类型 LED,便于维护与 IDE 自动补全; - ✅ 空安全增强:
SetZone1Status中增加led != null判空,防止NullPointerException。
⚠️ 注意事项:
- 若业务要求返回的
LinkedList必须保持特定顺序(如 zone1→zone8→tamper→…),可在GetChangedLedObjects()中按预定义键序列遍历:List<String> order = Arrays.asList("zone1","zone2",..., "pa"); for (String key : order) { LED led = ledMap.get(key); if (led != null && led.updateRequired) result.add(led); } -
LED类建议补充构造函数重载与必要 getter/setter,提升封装性; - 生产环境应配合单元测试验证
StateHasChanged()与GetChangedLedObjects()在边界场景(如全 false、全 true、空 map)下的行为。
综上,该内存溢出本质是数据结构误用引发的逻辑死循环。采用 Map 驱动的设计不仅根治问题,更使代码符合开闭原则——新增 LED 类型只需扩展枚举,无需修改初始化逻辑。

















