如何有效地从数组列表或字符串数组中删除所有空元素?

我试着像那样绕圈

// ArrayList tourists


for (Tourist t : tourists) {
if (t != null) {
t.setId(idForm);
}
}

但这并不好。谁能给我一个更好的解决办法?


一些有用的基准可以帮助你做出更好的决定:

While循环,For循环和迭代器性能测试

224787 次浏览
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
if (itr.next() == null) { itr.remove(); }
}

试一试:

tourists.removeAll(Collections.singleton(null));

读取Java API。代码将为不可变列表抛出java.lang.UnsupportedOperationException(例如用Arrays.asList创建的列表);更多细节见这个答案

效率不高,但时间短

while(tourists.remove(null));

我摆弄了一下,发现trimToSize()似乎可以工作。我是在Android平台上工作,所以可能会有所不同。

有一种简单的方法可以从collection中删除所有null值。必须将包含null的集合作为参数传递给removeAll()方法

List s1=new ArrayList();
s1.add(null);


yourCollection.removeAll(s1);

如果你更喜欢不可变的数据对象,或者你不想破坏输入列表,你可以使用Guava的谓词。

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))
list.removeAll(Collections.singleton(null));

如果你在数组上使用它,它将抛出UnsupportedException。asList,因为它给你不可变的副本,所以它不能修改。请参阅下面的代码。它创建可变的副本,并且不会抛出任何异常。

public static String[] clean(final String[] v) {
List<String> list = new ArrayList<String>(Arrays.asList(v));
list.removeAll(Collections.singleton(null));
return list.toArray(new String[list.size()]);
}

这是从数组列表中删除默认空值的简单方法

     tourists.removeAll(Arrays.asList(null));

从数组列表中删除字符串值“null”

       tourists.removeAll(Arrays.asList("null"));

我们可以使用迭代器for the same删除所有空值。

Iterator<Tourist> itr= tourists.iterator();
while(itr.hasNext()){
if(itr.next() == null){
itr.remove();
}
}

截至2015年,这是最好的方法(Java 8):

tourists.removeIf(Objects::isNull);

注意:此代码将为固定大小的列表抛出java.lang.UnsupportedOperationException(例如使用Arrays.asList创建的列表),包括不可变列表。

我使用流接口和流操作收集以及一个helper方法来生成一个新列表。

tourists.stream().filter(this::isNotNull).collect(Collectors.toList());


private <T> boolean isNotNull(final T item) {
return  item != null;
}

Objects类有一个nonNull Predicate,可以与filter一起使用。

例如:

tourists.stream().filter(Objects::nonNull).collect(Collectors.toList());

在Java 8中,你可以使用stream()filter()来做到这一点

tourists = tourists.stream().filter(t -> t != null).collect(Collectors.toList())

tourists = tourists.stream().filter(Objects::nonNull).collect(Collectors.toList())

更多信息:Java 8 - Streams

在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(),它有一个读位置和一个写位置。并迭代列表。当它到达null时,它只是将读位置迭代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()基本上是免费的,但这种形式的delete是一个有用的工具
for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
if (itr.next() == null) { itr.remove(); }
}

虽然这看起来很合理,但迭代器上的.remove()在内部调用:

ArrayList.this.remove(lastRet);

同样是O(n)操作。它执行system。arraycopy()如果你关心速度,这也不是你想要的。它是n²。

还有:

while(tourists.remove(null));
也就是O(m*n²)这里我们不仅迭代列表。每次匹配null值时,我们都会重复整个列表。然后我们执行n/2次(平均)操作来执行System.arraycopy()来执行删除。 从字面上看,您可以在有值的项和有空值的项之间对整个集合进行排序,并在更短的时间内修剪结尾。事实上,所有破碎的人都是这样。至少理论上,实际系统是这样的。arraycopy实际上并不是一个N操作。在理论上,理论和实践是一回事;

使用Java 8可以使用流、并行流和removeIf方法以各种方式执行此操作:

List<String> stringList = new ArrayList<>(Arrays.asList(null, "A", "B", null, "C", null));
List<String> listWithoutNulls1 = stringList.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList()); //[A,B,C]
List<String> listWithoutNulls2 = stringList.parallelStream()
.filter(Objects::nonNull)
.collect(Collectors.toList()); //[A,B,C]
stringList.removeIf(Objects::isNull); //[A,B,C]

并行流将利用可用的处理器,并将加快合理大小列表的处理速度。在使用流之前进行基准测试总是明智的。

类似于@Lithium的答案,但不抛出“列表可能不包含类型null”错误:

   list.removeAll(Collections.<T>singleton(null));
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);

我主要用这个:

list.removeAll(Collections.singleton(null));

但是在我学习了Java 8之后,我改用了这个:

List.removeIf(Objects::isNull);