如何在自定义ExpandableArray中区分有意存储的null与默认null?
更优解决方案推荐
针对你遇到的问题,这里有几个比维护intentionedNullIndexes数组更简洁高效的方案:
方案1:用布尔数组标记有意null
内部维护一个和存储数组同长度的boolean[] isIntendedNull数组,用来标记对应索引是否是有意设置的null:
- 调用
replace(int index, Product p)时:- 如果
p == null,将isIntendedNull[index] = true; - 如果
p != null,将isIntendedNull[index] = false;
- 如果
- 调用
add(Product p)时,遍历数组,找到第一个满足element[index] == null && !isIntendedNull[index]的位置,将p存入该位置。
优点:实现简单,内存开销小(布尔数组每个元素仅占1字节,远小于存int索引的数组),查询和修改都是O(1)操作。
缺点:需要额外维护一个数组,和存储数组同步扩容(当原数组扩容时,布尔数组也要跟着扩容,新扩容的位置默认设为false,因为这些是未被使用的自然null)。
方案2:用包装类封装状态
定义一个内部包装类,把元素值和是否为有意null的状态绑定:
private static class Entry { Product value; boolean isIntendedNull; Entry() { this.value = null; this.isIntendedNull = false; // 默认是未使用的null,不是有意设置的 } }
内部存储数组改为Entry[] entries,初始化时每个Entry都是默认状态:
- 调用
replace(int index, Product p)时:- 如果
p == null,设置entries[index].value = null,同时entries[index].isIntendedNull = true; - 如果
p != null,设置entries[index].value = p,同时entries[index].isIntendedNull = false;
- 如果
- 调用
add(Product p)时,遍历数组,找到第一个entries[index].value == null && !entries[index].isIntendedNull的位置,更新该Entry的value为p。
优点:封装性更好,不用单独维护两个数组,扩容时只需处理entries数组即可,逻辑更统一。
缺点:相比布尔数组,每个Entry会多占用一点内存(但JVM会做对象头优化,实际开销不大)。
方案3:用HashSet存储有意null的索引
内部维护一个HashSet<Integer> intendedNullIndexes,专门存储被有意设为null的索引:
- 调用
replace(int index, Product p)时:- 如果
p == null,将index加入集合; - 如果
p != null,从集合中移除index(如果存在);
- 如果
- 调用
add(Product p)时,遍历数组,找到第一个element[index] == null && !intendedNullIndexes.contains(index)的位置,存入p。
优点:当有意null的数量较少时,内存开销远小于数组方案;无需同步扩容集合(数组扩容后,新索引默认不在集合里,符合预期)。
缺点:查询contains的时间复杂度是O(1)(HashSet的特性),但遍历数组的逻辑和之前一致;如果有意null的数量接近数组长度,内存开销会比布尔数组大。
方案选择建议
- 如果你的ExpandableArray扩容频繁且有意null的数量较多,优先选方案1;
- 如果更看重代码的封装性和可读性,优先选方案2;
- 如果有意null的数量通常很少,优先选方案3。
内容的提问来源于stack exchange,提问作者Nathan Ikazuyir
相关产品推荐
相关产品推荐

