C#集合遍历时删除和增加元素的方法
作者:codingsilence 发布时间:2021-12-11 18:53:24
本文实例讲述了C#集合遍历时删除和增加元素的方法。分享给大家供大家参考,具体如下:
大多数时候,遍历集合元素的时候并不需要对元素进行增加或者删除操作,但有些时候则需要,比如,如果集合中盛放的元素是社会上所有的人,那么有人死亡则元素删除,有人出生则是集合元素的增加。对于这种情况,遍历不能按照原来那种方式去做了,而且C#中的集合对于这类有增删动作的遍历,也不支持foreach循环。
有三种办法可以解决这一问题。
第一种方法:使用C#的LinkedList<>双链表。我原来设想,把原来链表需要删除的元素直接remove掉,那些新添加的元素,先装入到一个临时链表中,等循环结束,再用Add把临时链表的头结点添加到原来链表的尾部即可,这样算法的复杂度也较低,但是,出乎意料的是,C#的双链表,无法将属于另外一个链表的结点添加到本链表中,其Next属性也只读。无奈,只能一边循环,一边在原链表尾端添加结点,这样就需要标记处循环结束的位置,即需要在原来的未改动的链表的尾部结点处结束循环,而不是在改动后的链表的尾部结点处结束。这样就要求在循环开始之前,先获得尾部结点的引用。程序如下(链表中有0-29的整数值结点,遍历时遇到3的整数倍,就在链表尾端添加一个0值结点,遇到2的整数倍就删除结点)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace 集合遍历时删除或增加元素
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private LinkedList<int> list = new LinkedList<int>();
//初始化,添加0-29的整数进入链表
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 30; i++)
{
this.list.AddLast(i);
}
}
//遍历链表,做如下操作:
//遇到能被3整除的,就在该链表后增加一个0元素,遇到能被2整除的就删除该元素
private void button2_Click(object sender, EventArgs e)
{
LinkedListNode<int> nodeNow = this.list.First;//链表第一个元素
LinkedListNode<int> nodeLast = this.list.Last;//原链表的最后一个元素,循环结束的标记
LinkedListNode<int> nodeTmp;//临时结点
//循环结束的条件是,等当前结点是原链表的最后一个结点
while (nodeNow != nodeLast)
{
//如果能被3整除时,则在链表后加一个0
if (nodeNow.Value % 3 == 0)
{
this.list.AddLast(0);
}
//如果能被2整除,则删除该元素
if (nodeNow.Value % 2 == 0)
{
//如果nodeNow被删除了,那么一定不能用Next获取下一个要判断的元素
//因为已经自动向下一个移动了,这是就要在删除nodeNow之前,
//获取它的Next,赋给nodeTmp,待nodeNow删除之后,再把nodeTmp的内存赋给nodeNow
nodeTmp = nodeNow.Next;
this.list.Remove(nodeNow);
nodeNow = nodeTmp;
}
else
{
//如果不能被2整除,则在链表中保留该元素,并获得下一个并进行判断
nodeNow = nodeNow.Next;
}
}
//最后不要忘记对nodeLast(原链表最后一个元素)本身进行处理,上面的while循环没有包括这个nodeLast
if (nodeNow.Value % 3 == 0)
{
this.list.AddLast(0);
}
if (nodeNow.Value % 2 == 0)
{
this.list.Remove(nodeNow);
}
}
//测试结果
private void button3_Click(object sender, EventArgs e)
{
foreach (int i in this.list)
{
Console.WriteLine(i);
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace 集合遍历时删除或增加元素
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private LinkedList<int> list = new LinkedList<int>();
//初始化,添加0-29的整数进入链表
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 30; i++)
{
this.list.AddLast(i);
}
}
//遍历链表,做如下操作:
//遇到能被3整除的,就在该链表后增加一个0元素,遇到能被2整除的就删除该元素
private void button2_Click(object sender, EventArgs e)
{
LinkedListNode<int> nodeNow = this.list.First;//链表第一个元素
LinkedListNode<int> nodeLast = this.list.Last;//原链表的最后一个元素,循环结束的标记
LinkedListNode<int> nodeTmp;//临时结点
//循环结束的条件是,等当前结点是原链表的最后一个结点
while (nodeNow != nodeLast)
{
//如果能被3整除时,则在链表后加一个0
if (nodeNow.Value % 3 == 0)
{
this.list.AddLast(0);
}
//如果能被2整除,则删除该元素
if (nodeNow.Value % 2 == 0)
{
//如果nodeNow被删除了,那么一定不能用Next获取下一个要判断的元素
//因为已经自动向下一个移动了,这是就要在删除nodeNow之前,
//获取它的Next,赋给nodeTmp,待nodeNow删除之后,再把nodeTmp的内存赋给nodeNow
nodeTmp = nodeNow.Next;
this.list.Remove(nodeNow);
nodeNow = nodeTmp;
}
else
{
//如果不能被2整除,则在链表中保留该元素,并获得下一个并进行判断
nodeNow = nodeNow.Next;
}
}
//最后不要忘记对nodeLast(原链表最后一个元素)本身进行处理,上面的while循环没有包括这个nodeLast
if (nodeNow.Value % 3 == 0)
{
this.list.AddLast(0);
}
if (nodeNow.Value % 2 == 0)
{
this.list.Remove(nodeNow);
}
}
//测试结果
private void button3_Click(object sender, EventArgs e)
{
foreach (int i in this.list)
{
Console.WriteLine(i);
}
}
}
}
第二种方法:使用C#的List<>,List<>是基于数组的顺序表,增加、删除动作时间复杂度较高,不如链表的效率高。其基本原来同第一种方法相似,也需要使用一个int型的变量标记原顺序表的尾部元素,当删除一个元素时,这个变量需要自减。代码略。
第三种方法,自定义单链表泛型类(链表类见https://www.jb51.net/article/87610.htm)。跟第一种方法比的好处,就是能够灵活实现两个链表的合并,只需要把第二个链表的头结点设置成第一个链表的尾结点的Next的结点(或直接Add)就可以了。其实对于C#的双链表,我并不是很清楚,为什么AddLast()方法,无法将一个链表的元素添加到另一个链表中,而只能添加一个不属于任何链表的结点(有人说第一种方法,其实可以使用结点Clone,但是这样无非还是增加算法的空间和时间复杂度,违背了使用链表的本意)。C#之所以不支持这种做法的原因可能是,MS担心你加入的结点,位于一个环状链表上,这样会导致原链表的Last属性、Count属性等无法计算(形成死循环)。测试代码如下:
//两个链表的合并
LinkedList<int> list = new LinkedList<int>();
for (int i = 0; i < 10; i++)
{
list.Add(i);
}
LinkedList<int> list2 = new LinkedList<int>();
for (int i = 10; i < 20; i++)
{
list2.Add(i);
}
list.Add(list2.Head);
Node<int> n = list.Head;
while(n!=null)
{
Console.WriteLine(n.Data);
n = n.Next;
}
Console.ReadLine();
Console.WriteLine(list.GetLength());
Console.ReadLine();
//两个链表的合并
LinkedList<int> list = new LinkedList<int>();
for (int i = 0; i < 10; i++)
{
list.Add(i);
}
LinkedList<int> list2 = new LinkedList<int>();
for (int i = 10; i < 20; i++)
{
list2.Add(i);
}
list.Add(list2.Head);
Node<int> n = list.Head;
while(n!=null)
{
Console.WriteLine(n.Data);
n = n.Next;
}
Console.ReadLine();
Console.WriteLine(list.GetLength());
Console.ReadLine();
希望本文所述对大家C#程序设计有所帮助。


猜你喜欢
- 本文实例为大家分享了Android Studio实现简单计算器功能的具体代码,供大家参考,具体内容如下程序步骤:(1)在布局文件定义一些计算
- 问题描述本来开发工具安装的是IDEA2018,有天用着用着突然崩溃了,重启后死活用不了。心血来潮下载了2019版本,顺利安装完,但是点击快捷
- 本文实例讲述了Android开发实现横向列表GridView横向滚动的方法。分享给大家供大家参考,具体如下:Android 横向列表实现,可
- 本文实例为大家分享了C语言实现学生信息管理系统的具体代码,供大家参考,具体内容如下#define _CRT_SECURE_NO_WARNIN
- 看到很多人解析歌词文件时写了一大片的字符处理代码,而且看得不是很明白,所以自己研究了一下, 首先来了解下Lrc文件 时间
- 1. 算法分析根据概率将奖品划分区间,每个区间代表一个奖品,然后抽取 随机数,反查落在那个区间上,即为所抽取的奖品。2. 代码核心
- springboot 长轮询实现基于 @EnableAsync , @Sync@SpringBootApplication@EnableAs
- 布局布局效果如下,下面每个“网格”都是一个按钮,点击按钮,就会有相应的事件发生。由于UniformG
- 说明本实例能够监控聚划算的抢购按钮,在聚划算整点聚的时间到达时自动弹开页面(URL自己定义)。可以自定义监控持续分钟数,同时还可以通过多线程
- 没有接触过音乐字幕方面知识的话,会对字幕的实现比较迷茫,什么时候转到下一句?看了这篇文章,你就会明白字幕so easy。先来一张效果图:字幕
- 前言注解是Java很强大的部分,但大多数时候我们倾向于使用而不是去创建注解。例如,在Java源代码里不难找到Java编译器处理的@Overr
- 一. 为什么需要比较对象上一节介绍了优先级队列,在优先级队列中插入的元素必须能比较大小,如果不能比较大小,如插入两个学生类型的元素,会报Cl
- 本文实例为大家分享了Java swing读取txt文件实现学生考试系统的具体代码,供大家参考,具体内容如下主要实现了一个简单的倒计时答题系统
- 什么是适配器模式? 适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不
- 文件资源操作Spring 定义了一个 org.springframework.core.io.Resource 接口,Resource 接口
- 前言poi的解析方式是dom解析,把结果一次都读入内存操作,这样的操作平时是不会有问题的,但是并发量上来的时候就会出现OOM,EasyExc
- 使用filter对request body参数进行校验@Slf4jpublic class ParameterCheckServletReq
- 这几天做项目,有些地方的图片需要用到圆形图片,所以百度了一下,在github上找到一个开源项目,处理很简单,效果如下: 使用起来特
- ViewPager2 介绍ViewPager2 是基于 RecyclerView 重新编写的 ViewPager,比原有的 ViewPage
- 场景描述单例模式对于我们来说一点也不模式,是一个常见的名称,单例模式在程序中的实际效果就是:确保一个程序中只有一个实例,并提供一个全局访问点