循环链表---对象赋值的相关问题

  • 问题引入
  1. 单向循环链表节点的定义:
循环链表---对象赋值的相关问题
 1     public class CirNode<T>
 2     {
 3         public T Item { get; set; }
 4         public CirNode<T> Next { get; set; }
 5 
 6         public CirNode()
 7         {
 8         }
 9 
10         public CirNode(T item)
11         {
12             this.Item = item;
13         }
14     }
View Code

  2.单向循环链表的模拟实现:

循环链表---对象赋值的相关问题
  1     /// <summary>
  2     /// 单向循环链表的模拟实现
  3     /// </summary>
  4     public class MyCircularLinkedList<T>
  5     {
  6         private int count; // 字段:记录数据元素个数
  7         private CirNode<T> tail; // 字段:记录尾节点的指针
  8         private CirNode<T> currentPrev; // 字段:使用前驱节点标识当前节点
  9 
 10         // 属性:指示链表中元素的个数
 11         public int Count
 12         {
 13             get
 14             {
 15                 return this.count;
 16             }
 17         }
 18 
 19         // 属性:指示当前节点中的元素值
 20         public T CurrentItem
 21         {
 22             get
 23             {
 24                 return this.currentPrev.Next.Item;
 25             }
 26         }
 27 
 28         public MyCircularLinkedList()
 29         {
 30             this.count = 0;
 31             this.tail = null;
 32         }
 33 
 34         public bool IsEmpty()
 35         {
 36             return this.tail == null;
 37         }
 38 
 39         // Method01:根据索引获取节点
 40         private CirNode<T> GetNodeByIndex(int index)
 41         {
 42             if (index < 0 || index >= this.count)
 43             {
 44                 throw new ArgumentOutOfRangeException("index", "索引超出范围");
 45             }
 46 
 47             CirNode<T> tempNode = this.tail.Next;
 48             for (int i = 0; i < index; i++)
 49             {
 50                 tempNode = tempNode.Next;
 51             }
 52 
 53             return tempNode;
 54         }
 55 
 56         // Method02:在尾节点后插入新节点
 57         public void Add(T value)
 58         {
 59             CirNode<T> newNode = new CirNode<T>(value);
 60             if (this.tail == null)
 61             {
 62                 // 如果链表当前为空则新元素既是尾头结点也是头结点
 63                 this.tail = newNode;
 64                 this.tail.Next = newNode;
 65                 this.currentPrev = newNode;
 66             }
 67             else
 68             {
 69                 // 插入到链表末尾处
 70                 newNode.Next = this.tail.Next;
 71                 this.tail.Next = newNode;
 72                 // 改变当前节点
 73                 if (this.currentPrev == this.tail)
 74                 {
 75                     this.currentPrev = newNode;
 76                 }
 77                 // 重新指向新的尾节点
 78                 this.tail = newNode;
 79             }
 80             Console.WriteLine(this.currentPrev.Next.Item);
 81             this.count++;
 82         }
 83 
 84         // Method03:移除当前所在节点
 85         public void Remove()
 86         {
 87             if (this.tail == null)
 88             {
 89                 throw new NullReferenceException("链表中没有任何元素");
 90             }
 91             else if (this.count == 1)
 92             {
 93                 // 只有一个元素时将两个指针置为空
 94                 this.tail = null;
 95                 this.currentPrev = null;
 96             }
 97             else
 98             {
 99                 if (this.currentPrev.Next == this.tail)
100                 {
101                     // 当删除的是尾指针所指向的节点时
102                     this.tail = this.currentPrev;
103                 }
104                 // 移除当前节点
105                 this.currentPrev.Next = this.currentPrev.Next.Next;
106             }
107 
108             this.count--;
109         }
110 
111         // Method04:获取所有节点信息
112         public string GetAllNodes()
113         {
114             if (this.count == 0)
115             {
116                 throw new NullReferenceException("链表中没有任何元素");
117             }
118             else
119             {
120                 CirNode<T> tempNode = this.tail.Next;
121                 string result = string.Empty;
122                 for (int i = 0; i < this.count; i++)
123                 {
124                     result += tempNode.Item + " ";
125                     tempNode = tempNode.Next;
126                 }
127 
128                 return result;
129             }
130         }
131     }
View Code

其中循环链表新节点的插入实现方法中,用了对象赋值给另一个对象:

 1         public void Add(T value)
 2         {
 3             CirNode<T> newNode = new CirNode<T>(value);
 4             if (this.tail == null)
 5             {
 6                 // 如果链表当前为空则新元素既是尾头结点也是头结点
 7                 this.tail = newNode;//@1
 8                 this.tail.Next = newNode;//@2
 9                 this.currentPrev = newNode;//@3
10             }
11             else
12             {
13                 // 插入到链表末尾处
14                 newNode.Next = this.tail.Next;
15                 this.tail.Next = newNode;
16                 // 改变当前节点
17                 if (this.currentPrev == this.tail)
18                 {
19                     this.currentPrev = newNode;
20                 }
21                 // 重新指向新的尾节点
22                 this.tail = newNode;
23             }
24             Console.WriteLine(this.currentPrev.Next.Item);
25             this.count++;
26         }
  • 问题原因

其中@1处,把新new的一个对象newNode赋值给this.tail,此时这两个对象引用的是同一个内存地址空间,newNode.Next=null,this.tail.Next也是为Null,而到@2这一步时,给this.tail.Next赋值为newNode时,同时也会对newNode进行操作既给newNode.Next赋值,因为这两个对象引用的是同一个地址空间。

  • 问题引申

浅复制和深复制:

浅复制:

 1  public class Test
 2  {
 3       public string A { get; set; }
 4       public int B { get; set; }
 5       public override string ToString()
 6       {
 7           return $"A:{A},B:{B}";
 8       }
 9  }
10     static void Main(string[] args)
11       {
12              var list = new List<Test>()
13              {
14                  new Test(){A="A1",B=1 },
15                  new Test(){ A="A2",B=2}
16              };
17              var list_Copy = list.ToList();
18              list_Copy[0].A = "这是修改后的值";
19              foreach (var i in list)
20              {
21                  Console.WriteLine(i);
22              }
23              Console.Read();
24       }

执行结果:循环链表---对象赋值的相关问题

由上图可以看出,这里与循环链表里面的插入方法中的@1是一样的,修改list_Copy的元素的属性值,list中的元素的属性也跟着变了。

 

深复制:

 1  public class Test:ICloneable
 2     {
 3         public string A { get; set; }
 4         public int B { get; set; }
 5 
 6         public object Clone()
 7         {
 8             return this.MemberwiseClone();
 9         }
10 
11         public override string ToString()
12         {
13             return $"A:{A},B:{B}";
14         }
15     }
16     static void Main(string[] args)
17         {
18             var list = new List<Test>()
19             {
20                 new Test(){A="A1",B=1 },
21                 new Test(){ A="A2",B=2}
22             };
23             var list_Copy = new List<Test>();
24             list.ForEach(x => list_Copy.Add(x.Clone() as Test));
25             list_Copy[0].A = "这是修改后的值";
26             foreach (var i in list)
27             {
28                 Console.WriteLine(i);
29             }
30             Console.Read();
31         }

运行结果:循环链表---对象赋值的相关问题

可以看出,修改list_Copy的任何元素任何属性,都不会影响到list,但是list与list_Copy中的元素值是相同的,这就是深复制。

深复制还可以借用json:

1 public static void Copy<T>(T source, ref T destination) where T : class
2   {
3         string jsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(source);
4         destination = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonStr);
5   }

 

参考文章链接:

  1. https://www.cnblogs.com/edisonchou/p/4614934.html
  2. https://blog.csdn.net/lishuangquan1987/article/details/86085971
上一篇:关于64位W7下怎么学习汇编语言的一些心得!


下一篇:java.util.Arrays.sort两种方式的排序(及文件读写练习)