你应该使用 Pre-Java 8:
tourists.removeAll(Collections.singleton(null));
Java 8 后使用:
tourists.removeIf(Objects::isNull);
这里的原因是时间复杂度。数组的问题是删除操作可能需要 O(n) 时间才能完成。实际上,在 Java 中,这是一个剩余元素的数组副本,被移动以替换空白点。此处提供的许多其他解决方案将触发此问题。前者在技术上是 O(n*m),其中 m 为 1,因为它是一个单例 null:所以 O(n)
您应该删除所有单例,在内部它执行一个具有读取位置和写入位置的 batchRemove()。并迭代列表。当它达到空值时,它只是将读取位置迭代 1。当它们相同时它通过,当它们不同时它继续复制值。然后在最后修剪到大小。
它在内部有效地做到了这一点:
public static <E> void removeNulls(ArrayList<E> list) {
int size = list.size();
int read = 0;
int write = 0;
for (; read < size; read++) {
E element = list.get(read);
if (element == null) continue;
if (read != write) list.set(write, element);
write++;
}
if (write != size) {
list.subList(write, size).clear();
}
}
您可以明确看到的是 O(n) 操作。
唯一可能更快的是,如果您从两端迭代列表,并且当您找到一个空值时,您将其值设置为您在最后找到的值,并减少该值。并迭代直到两个值匹配。你会弄乱顺序,但会大大减少值的数量
你设置与那些你一个人留下的。这是一个很好的了解方法,但在这里无济于事,因为 .set() 基本上是免费的,但这种形式的删除对你的腰带来说是一个有用的工具。
for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
if (itr.next() == null) { itr.remove(); }
}
虽然这看起来很合理,但迭代器上的 .remove() 在内部调用:
ArrayList.this.remove(lastRet);
这又是删除中的 O(n) 操作。如果您关心速度,它会执行一个 System.arraycopy() ,这又不是您想要的。这使它成为 n^2。
还有:
while(tourists.remove(null));
这是 O(m*n^2)。在这里,我们不仅迭代列表。每次匹配空值时,我们都会重复整个列表。然后我们执行 n/2(平均)操作来执行 System.arraycopy() 来执行删除。
从字面上看,您可以在具有值的项目和具有空值的项目之间对整个集合进行排序,并在更短的时间内修剪结尾。事实上,这对所有破碎的人来说都是如此。至少在理论上,实际的 system.arraycopy 实际上并不是 N 操作。理论上,理论和实践是一回事;实际上它们不是。