从集合中移除所有的空值

4

我需要应对从不受控制的数据源中传递给我的应用程序的数据集合。其中一些集合包含空值,我希望在它们到达我的代码之前尽早过滤掉它们,而不是在各个地方散布空值检查代码。我想以可重用的通用方式实现这一点,并编写了以下方法:

    public static void RemoveNulls<T>(this IList<T> collection) where T : class
    {
        for (var i = 0; i < collection.Count(); i++)
        {
            if (collection[i] == null)
                collection.RemoveAt(i);
        }
    }

我知道在具体的 List 类中,有一个名为 RemoveAll() 的方法可用于以下操作:
collection.RemoveAll(x => x == null);

但是很多返回类型都是基于接口的(IList/IList…),而不是具体的类型。


1
请问您能否详细说明为什么不能使用 RemoveAll 函数? - cuongle
1
那问题是什么? - Hossein Narimani Rad
2
你所编写的代码无法正常工作,因为删除一个元素会将所有后续元素的索引向下移动1个位置。每次删除一个空值时,你的代码都会跳过检查下一个元素。 - Jeremy Todd
这里的人可能会非常龟毛... - Jammer
@Jammer - 你有几个拼写错误,而且似乎你试图解决错误的问题。听起来你控制着放置在你的集合中的内容,这意味着你可以确定是否应该将该数据添加到集合中。 - Security Hound
2个回答

21

不必从源集合中删除 null 值,可以使用 LINQ 创建一个没有 null 值的集合副本:

collection.Where(i => i != null).ToList();

扩展方法适用于任何IEnumerable,包括IList。


不必要地复制整个集合是为什么? - Jammer
3
@Jammer,你可以放弃 .ToList() 部分,以便它可以作为一个过滤器使用。如果你关心性能,请记住每个 RemoveAt 都会移动被删除项之后的所有列表项。所以,如果你从具有 1000 个项的列表开头移除 10 个项,至少会有 990 个项在内存中被移动 10 次。 - alex
1
它实际上可能会提高性能,因为从IList(至少具有List<T>这样的数组支持)中删除元素每次都会强制将所有后续元素下移一个插槽进行复制。 - Jeremy Todd
@Jammer 我建议你使用这个解决方案和原地解决方案进行一些性能测试。如果你的每个集合平均有两个以上的空值,我敢打赌这个解决方案会运行得更快。 - Dax Fohl

4

你的方法无法奏效,因为删除一个元素会导致所有后续元素的索引递减。如果您不想使用 Linq 解决方案(看一下 @alex 的答案似乎最简单),您应该向后迭代。

public static void RemoveNulls<T>(this IList<T> collection) where T : class
{
    for (var i = collection.Count-1; i >= 0 ; i--)
    {
        if (collection[i] == null)
            collection.RemoveAt(i);
    }
}

在这里进行测试完全没有问题 - 尝试几个更多的测试用例。特别是,一个包含两个连续的 null 值,后面跟着一个非 null 值的集合。 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接