读书人

对集合遍历并删除的有关问题

发布时间: 2012-09-04 14:19:30 作者: rapoo

对集合遍历并删除的问题
我的代码如下,但是这样 一旦真的删除了对象,那么出错, 请问怎么遍历集合 然后根据条件删除元素?

C# code
  for (int i = 0; i < AppConfig.MyTcpClient.Count; i++ )                        {                            TcpClient c = AppConfig.MyTcpClient[i];                            if (c == null || c.Connected == false)                            {                                AppConfig.MyTcpClient.Remove(c);                            }                            else                            {                                User u = new User();                                u.name = c.Client.RemoteEndPoint.ToString();                                u.MyTcpClient = c;                                AppConfig.MainFrom.combobox.InvokeIfNeeded((x) => AppConfig.MainFrom.combobox.Items.Add(x), u);                            }                        }


[解决办法]
for (int i = 0; i < AppConfig.MyTcpClient.Count; i++ )
{
TcpClient c = AppConfig.MyTcpClient[i];
if (c == null || c.Connected == false)
{
AppConfig.MyTcpClient.Remove(c);
i--;
}
else
{
[解决办法]
这是很基础的操作吧
你这么做肯定是错误的,你删一个就跳过了一个。
比如删掉了3号元素,原来的4号就变成3号了,而你的索引又递增到4,于是原来的4号就被跳过了。
你可以手动地每删一次就把索引复位一次。
或者从后往前遍历并删除,这样就不存在索引的问题了。我以前初学VB6的时候就是用的这种方法。

不过对于List<T>,T[]等结构来说,这些方法都不好,因为你删一个元素就要把后面所有的元素往前移,这个操作非常耗时。(比如一个列表有100W元素,你删除原来的0号元素,就要把后面100W-1个元素全部前移;然后你再删除原来的1号元素,又要把后面的100W-1-1个元素全部前移……)

一种比较常用的方法是把需要保留的元素移动到列表前端(需要删除的不管,被需要保留的元素覆盖掉),最后缩减容器的长度,这样可以大大减少数据移动的次数(只有需要保留的元素会被移动一次,而上面那种方法需要保留的元素和不需要保留的元素都会被多次移动)。

说了这么多,如果你的容器是List<T>,用List的RemoveAll方法就行了,它用的就是上面那一段描述的方法,效率比你自己一个个删高得多,代码还简洁明了。

当然,如果一开始就预见到增删操作比较频繁,而不怎么要求随机访问,换成链表是最好的。
[解决办法]
遍历一遍 把要删除的罗列出来 再遍历新数组 移除原来的数组
[解决办法]
for (int i = AppConfig.MyTcpClient.Count-1; i >=0; i-- )
{
TcpClient c = AppConfig.MyTcpClient[i];
if (c == null || c.Connected == false)
{
AppConfig.MyTcpClient.Remove(c);
i--;
}
else
{
[解决办法]
探讨

for (int i = AppConfig.MyTcpClient.Count-1; i >=0; i-- )
{
TcpClient c = AppConfig.MyTcpClient[i];
if (c == null || c.Connected == false)
{
AppConfig.MyTcpClient.Remove(c);
i--;
……

[解决办法]
探讨

for (int i = 0; i < AppConfig.MyTcpClient.Count; i++ )
{
TcpClient c = AppConfig.MyTcpClient[i];
if (c == null || c……

[解决办法]
探讨

肯定是不能用RemoveAll ,我又不是删除全部,只是不满足条件了的对象 才删除.
可以预见的增减是很频繁的, 你说的用链表 是怎么弄的?

引用:
这是很基础的操作吧
你这么做肯定是错误的,你删一个就跳过了一个。
比如删掉了3号元素,原来的4号就变成3号了,而你的索引又递增到4,于是原来的4号就被跳过了。
你可以手动地每删一次就把索引复位一次。
或者从后往前……

[解决办法]
安全的写法是:

for (int i = 0; i < AppConfig.MyTcpClient.Count; i++ )


{
TcpClient c = AppConfig.MyTcpClient[i];
if (c == null || c.Connected == false)
{
AppConfig.MyTcpClient.Remove(c);
i--;
continue;//这样
}
else
{
User u = new User();

u.name = c.Client.RemoteEndPoint.ToString();
u.MyTcpClient = c;

AppConfig.MainFrom.combobox.InvokeIfNeeded((x) => AppConfig.MainFrom.combobox.Items.Add(x), u);
}
}

[解决办法]
1.要看你集合的类型
2.删除频繁否(得到回答是频繁)
3.集合多大
4.每次删除的的数量有多大。
链表,并且删除的数目小于留下的数目,14楼的不错!
如果删除的数目很多,直接把不需要删除的拷贝到新链表里去。

还有个问题,用链表的话,每次查询元素,效率比较低,不知道其他地方是保存 socket的ID还是引用?
[解决办法]
TcpClient c = AppConfig.MyTcpClient[i];
这个查询 应该也比较慢
[解决办法]
lz的写法应该加上i--

读书人网 >C#

热点推荐