检查一个向量是否为空

54

假设我有一个名为Vectorstd::vector

现在在向量上执行一些操作(插入或删除),我想检查向量是否为空,然后根据此执行某些操作。

哪种方法更好?

方法1

if (Vector.size() == 0){ /* operations */ }

方法二

if (Vector.empty()) { /* operations */ }

哪种方法更好,1 还是 2


https://dev59.com/KXRB5IYBdhLWcg3wCjcV - x29a
8个回答

58

v.size() == 0表示“我正在比较大小”,但实际上是为了检查容器是否为空。在你理解它的含义之前,需要消化一个小算法(非常小,因为它只包括一次比较)。

另一方面,v.empty()正好做了它所说的事情:它检查v是否为空。

由于这个原因,我明显更喜欢第二种方法,因为它做了它所说的事情。这就是为什么empty()被发明出来的原因。

但是也有一个算法上的原因更喜欢empty():如果将std::vector更改为std::listv.size()可能具有O(n)的时间复杂度。(在C++ 03中,对于std::vector,它保证是O(1),但对于std::list不是。根据James在Prasoon的回答中的评论,对于所有容器在C++1x中都将是O(1)。)


1
回复您下面的问题/评论,Howard Hinnant撰写了一份白皮书,讨论了list的权衡取舍(实际上是splice()速度与size()速度之间的权衡)。我现在似乎无法获取到这份白皮书,但是Michael Burr对其进行了总结。 - James McNellis
@Donotalo:这是即将获得批准的新C++标准。有一段时间它被称为“C++0x”(其中0x表示“在[2000,2010]范围内的某个年份”),但由于他们未能在2010年之前获得批准,现在它也被称为“C++1x”。一些人仍然称其为C++0x,因为它曾经被称为这样,或者因为他们认为那个“x”应该被理解为十六进制数字。 - Matteo Italia
就我而言,底线是:C++0x不是官方名称。它是一个占位符,一个用于标识尚未标准化的草案的昵称。因此,它不需要更新。他们可以称之为C++1643,它也能正常工作。因此,直到它被标准化并获得官方名称的那一天,我将继续称其为C++0x。 - jalf
C++1x的缺点是你可以将C++0x发音为“See-tox”。 - buildsucceeded
empty() 的问题在于英语中的歧义性。不清楚“empty”是动词还是形容词! - sergiol
显示剩余4条评论

15

第二种方法更好,因为无论容器类型如何,empty()总是在常数时间内运行 [即 O(1)]。

size()对于std::vector也会在O(1)的时间内运行,但对于std:list则可能需要O(n)的时间运行[实现方式有所不同]

在《Effective STL》[Item 4]一书中,Scott Meyers表示

你应该优先选择使用 empty 的方法,原因很简单:对于所有标准容器来说,empty 都是一个常数时间(constant-time)操作,但对于某些list实现,size却需要线性时间。

.....

无论发生什么情况,如果你调用 empty 而不是检查 size() == 0,都不会出错。因此,每当您需要知道一个容器是否有零个元素时,请调用 empty。


4
不,size() 几乎可以保证是 O(1),尤其是对于 vector。在 C++0x 中,任何实现了 size() 的容器都必须保证其时间复杂度为 O(1)。 - James McNellis
1
@sbi:是的,所有容器都要实现。目前容器的要求只是说size()应该具有常数时间复杂度(这意味着根本没有复杂度要求)。C++0x对此进行了改变。当我在第一条评论中说“对于任何实现它的容器”时,我是错误的;所有容器都必须实现size() - James McNellis
@James:啊,我不知道那个。我得改一下我的答案。你知道这个变化的理由是什么吗?毕竟这是一个以空间换时间的权衡。 - sbi
@JamesMcNellis:专门为vector?! - sergiol

8
我会选择第二种方法,因为empty()方法是有意设计用于检查向量是否为空。您还可以检查两种方法的效率,然后决定哪种更好。

6
empty 方法特别适用于 std::list,因为计算它们的大小可能会非常耗时。 - Benoit
1
@Benoit:一些list的实现具有常数时间的size();在C++0x中,size()将被要求对于所有容器,包括list,具有常数时间复杂度。 - James McNellis
@James:如果你想要使用splice,并希望它是恒定时间,那么使用自定义或第三方列表的好原因之一。然后,又回到my_list::size为O(N)的情况。 - Potatoswatter
@James:你确定吗?正如Potatoswatter所说,这需要splice是O(n),但我非常确定它需要是O(1)。 - Oliver Charlesworth
1
@Oli:是的。从另一个列表中拼接一系列节点具有线性时间复杂度(所有其他拼接都具有常数时间复杂度)。这种复杂度要求在C++03和C++0x中是相同的。 - James McNellis
@Oli:我的意思是,即使标准没有提供这样的不兼容列表模板,也不能忽略它的需求。@James: +1 - Potatoswatter

3
实际上,vector.empty()和vector.size()==0是做同样的事情。 empty比较开头和结尾,如果它们相同则返回true,size计算begin - end,因此如果为空则返回0,因此使用另一种计算方式做同样的事情。

抱歉,您能重新表达一下您的答案吗?很难理解,请尝试分开句子并明确说明为什么它们是相同的。 - Armfoot
那不是在做同样的事情。结果可能相同,但它们远非在做同样的事情! - sergiol

3
通常,一个向量被内部实现为指向动态分配数组的指针,并且有数据成员保存向量的capacitysize。向量的size是实际元素的数量,而容量是动态数组的大小。
基于这种实现方式,成员函数size()将简单地获取成员sizeempty()将返回比较结果size == 0
因此,两者的效率都是相同的O(1),但如果要检查向量是否为空,则建议使用empty()。因为这就是该函数的作用。这将使您的代码更易于阅读。

2
如果你是新手程序员,建议使用对你来说更有意义的方法。例如,如果“==0”对你比“.empty()”更有意义,就使用前者。
稍后,如果你遇到了性能问题(我非常怀疑这里会出现性能问题),请使用符合你性能目标的方法。

5
我更希望我的同事,无论是新手还是老手,使用那个对有经验的程序员更有意义的选项。毕竟,我们都会在某个时候变得有经验。 - sbi
不仅如此,而且在长期性能方面更好的是(例如,如果您将来从std :: vector切换到std :: list)。 - Oliver Charlesworth
@sbi:你说得有道理 - 然而,代码很难阅读,你必须在这里和现在让它变得容易些。 - Daniel Mošmondor
@sbi:有些代码读起来就感觉很对,你不需要在脑海中翻译它。那就是你应该为自己编写的代码。 - Daniel Mošmondor
.empty() 的一个不好的地方是它并不清楚它是一种操作,将从容器中删除所有元素,还是告诉你容器是否为空。isEmpty() 更符合我的风格。但这就是标准库命名约定的本质。我必须遵循“入乡随俗”的规则;你应该优先考虑容器变化的更好不变式。因此,某人浏览代码库并用 x.empty() 替换所有 x.size() == 0 模式是有道理的——在这种情况下,我认为这是正确的答案。 - HostileFork says dont trust SE
显示剩余2条评论

1

只是为了好玩:为什么不呢:

if(Vector.begin() == Vector.end())

?


1

使用 empty()。


我(不)喜欢你对于你的观点的推理。 - sbi
1
关于向量的大小为空,应该不会有太多麻烦吧? :D - nakiya

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