循环删除容器中元素的方法和陷阱

算法大师Donald Knuth:不成熟的优化是一切恶果的根源(Permature optimization is the root of all evil )。

STL中的容器主要是两种:序列式容器和关联式容器。

下面讲到的都是我在开发中曾经遇到过的一些问题,有些例子我做了修改,我想初学STL的人基本都会遇到这些问题。

序列式容器:vector list string等等

关联式容器:set map等

1、容器的选择,到底要选什么容器应该根据你的需求和以后的扩展来选,可以参考effective stl上的某个条款,说的很清楚,我就不扯了。


--------------------------------------------------------------------------------

2、怎样正确的删除序列式容器中的数据(循环删除),这里以vevtor为例,至少有两大种方法,STL中任何容器都可以使用迭代器进行元素的遍历,当需要在遍历中删除某些元素时,容器中元素的布局(位置或者排序)会随之改变,当前迭代器所指示的元素也会发生变化,这时继续递增或者递减迭代器进行后续元素的遍历时就要特别小心。容器的元素删除方法很大程度上依赖于操作系统上STL的实现版本,不同的系统和STL版本均有不同的定义,当然我们这里以STL的标准来说明。

自己迭代实现删除:

vector<int>::iterator itVec = vectInt.begin();
// 删除所有值为1022的元素
for ( ; itVec != vectInt.end(); )
{
 // 删除
 if (*itVec == 1022)
  itVec = vectInt.erase(itVect);
 else
  ++itVec;
}

上面是正确的实现,容易犯的错误是在erase后还继续++inVect,这是错误的,vector内部实现erase之后itVect实际上已经指向了下一个元素,你再自增就会跳过一个元素,会引发什么问题就很难说了,如果刚好跳过了最后一个元素,还有可能程序在这个地方崩溃。list也可以用这种方法,同时list还可以用下面的方法,只有一点小改动:

list<int>::iterator itList = listInt.begin();
// 删除所有值为1022的元素
for ( ; itList != listInt.end(); )
{
 // 删除
 if (*itList == 1022)
  listInt.erase(itList++);
 else
  ++itList ;
}

vector不能用上面那种方法是因为vector内部是一块连续的内存,而list则是链表。

使用stl算法库中的remove remove_if来进行删除:

调用STL中算法 remove或者remove_if的陷阱,并没有真正的删除(你可以此时调用size方法来验证这一点),然后应该使用erase删除残余数据,remove或者remove_if的实现原理是用后面未被删除的元素 来覆盖前面应该被删除的元素。返回值ForwardIterator指向经移除后的最后元素的下一位置。如vector{0,1,2,3,3,4},执行remove(),希望移除所有值为3的元素,结果为{0,1,2,4,3,4},返回值ForwardIterator指向第5个元素。即:
  0 1 2 3 3 4 移除前
  0 1 2 4 3 4 移除后

这个方法对于自定义类型要自己实现访函数(函数对象),这里就不举例了。

两类方法的对比,其实后面的方法效率要好,如果数据大,用后面的方法,因为针对vector的话,用后面的方法不会像自己用循环去删除的时候,频繁的拷贝移动内存。


--------------------------------------------------------------------------------

3、关联式容器循环删除

for(iter = map.begin(); iter != map.end();)
{
 if (shouldDelete(*iter))
  map.erase(iter++);
 else
  ++iter;
}

因为关联式容器的erase并不返回下一个元素的迭代器,因此不能用iter = map.erase(iter)这样的表达式(有些版本的STL实现又可以这样写,我建议还是使用 map.erase(iter++)保证移植性)。

关联容器同样也可以用remove等来删除元素。


--------------------------------------------------------------------------------

4、谨慎对待操作STL容器的方法begin end size的优化,常见是将他们提取到循环的外部,如果你执行删除操作,可能导致错误,因为在进行删除操作的时候,容器的end,size很有可能随时变化。list容器的end一直不会变化,但是放在for里面,编译器也会优化。除非你能保证for循环中不会改变容器的大小。


--------------------------------------------------------------------------------

附录:

map使用:

第一种:用insert函数插入pair数据

Map<int, string> mapStudent;

mapStudent.insert(pair<int, string>(1, “student_one”));

my_Map.insert(make_pair("d",4));

第二种:用insert函数插入value_type数据

Map<int, string> mapStudent;

mapStudent.insert(map<int, string>::value_type (1, “student_one”));

第三种:用数组方式插入数据           

Map<int, string> mapStudent;

mapStudent[1] =  “student_one”;

mapStudent[2] = “student_two”


--------------------------------------------------------------------------------

 

转自:http://blog.sina.com.cn/s/blog_4ac0a0d30100q98z.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值