06---Net基础加强
字符串特性-池-不可变性
class Program { static void Main(string[] args) { #region 判断两个对象是否是同一个对象 //Person p1 = new Person(); //p1.Name = "叶长种"; //p1.Age = 16; //Person p2 = new Person(); //p2.Name = "叶长种"; //p2.Age = 16; ////p1与p2是否是同一个对象?No //Person p3 = p1; ////p1与p3是同一个对象吗?Yes //Console.ReadKey(); #endregion #region 字符串池特性 //Person p1 = new Person(); //p1.Name = "叶长种"; //p1.Age = 16; //Person p2 = new Person(); //p2.Name = "叶长种"; //p2.Age = 16; ////默认情况下可以调用对象的Equals方法来验证两个对象是否是同一个对象,但是由于该方法是可以重写的,所以子类有可能把该方法重写,当子类把该方法重写后,则通过该方法验证两个对象是否是同一个对象就不准确了,所以不要依赖Equals方法或==来判断两个对象是否是同一个对象,不准确!!!!! //Console.WriteLine(p1.Equals(p2)); //true //Console.WriteLine(p1 == p2); // false //Console.ReadKey(); //string[] names = new string[] { "张三", "李四", "王五" }; //string[] names1 = new string[] { "张三", "李四", "王五" }; //Console.WriteLine(names.Equals(names1));//false //Console.WriteLine(names == names1);//false //Console.ReadKey(); //string s1 = new string(new char[] { 'a', 'b', 'c' }); //string s2 = new string(new char[] { 'a', 'b', 'c' }); ////在字符串类中,Equals()与==都是判断字符串的内容是否相同而不是判断字符串的地址。 ////并且==也进行了运算符重载,==内部就是调用的Equals()方法来进行判断的。 //Console.WriteLine(s1.Equals(s2));//true //Console.WriteLine(s1 == s2);//true //Console.ReadKey(); ////判断两个对象是否是同一个对象的方法: ////object.ReferenceEquals(p1, p2)标准的判断对象是否是同一个对象的方法 //string s1 = new string(new char[] { 'a', 'b', 'c' }); //string s2 = new string(new char[] { 'a', 'b', 'c' }); //Console.WriteLine(object.ReferenceEquals(s1, s2)); //false //Console.ReadKey(); //Person p1 = new Person(); //p1.Name = "闫刘盘"; //p1.Age = 16; //Person p2 = new Person(); //p2.Name = "闫刘盘"; //p2.Age = 16; //Console.WriteLine(object.ReferenceEquals(p1, p2));//false //Console.ReadKey(); //string s1 = new string(new char[] { 'a', 'b', 'c' }); //string s2 = new string(new char[] { 'a', 'b', 'c' }); ////针对字符串常量的暂存池特性,对于相同的字符串常量,每次使用的时候并不会重新创建一个内存来存储,而是在第一次创建的时候,将字符串作为键,将字符串的地址作为值来存储,下次再用到的时候先去键值对中查找,如果有则直接返回上次创建字符串的地址。 这个特性依赖于字符串的另外一个特性才能存在:字符串的不可变性。 ////string s3 = "abc"; ////string s4 = "abc"; ////string s5 = "a" + "b" + "c"; //s3 s4 s5为同一个对象 //string a = "a"; //string b = "b"; //string c = "c"; //string s1 = "abc"; //string s2 = a + b + c; //这两个不为同一个对象 //Console.ReadKey(); ////string msg = "abcabcabcabcabcabcabcabcabcabcabc"; #endregion //int n = 10; #region 字符串的不可变性,字符串一旦创建,就不可以改变 //string s1 = "abc"; //s1 = "cba"; //Console.WriteLine(s1); //Console.ReadKey(); ////由于字符串的不可变性,所以造成了当多个字符串进行拼接的时候,造成内存的浪费以及时间的消耗(因为每次创建对象都要消耗时间,创建对象也是有成本的。) //string s = "a"; //s = s + "b"; //s = s + "c"; //s = s + "d"; //Console.WriteLine(s); //Console.ReadKey(); //string s1 = "abc"; //string s2 = new string(new char[] { 'a', 'b', 'c' }); #endregion #region 字符串的拼接 //00:00:07.1317142 //00:00:06.3386025 //00:00:06.6813519 //string msg = string.Empty; //string[] lines = File.ReadAllLines("sbTest.txt", Encoding.Default); //Stopwatch watch = new Stopwatch(); //watch.Start(); //for (int i = 0; i < lines.Length; i++) //{ // msg = msg + lines[i]; //} //watch.Stop(); //Console.WriteLine(watch.Elapsed); //Console.WriteLine("ok"); //Console.ReadKey(); #endregion #region StringBuilder进行字符串拼接 ////StringBuilder 每次修改都是修改同一个块内存中的值,不会重复创建大量对象,也不会产生垃圾内存,所以大大提高了字符串拼接的效率。建议大量字符串拼接的时候首选StringBuilder //StringBuilder msg = new StringBuilder(); //string[] lines = File.ReadAllLines("sbTest.txt", Encoding.Default); //Stopwatch watch = new Stopwatch(); //watch.Start(); //for (int i = 0; i < lines.Length; i++) //{ // msg.Append(lines[i]); //} //watch.Stop(); //Console.WriteLine(watch.Elapsed); ////把StringBuilder 转换为string 类型。 //string s = msg.ToString(); //Console.WriteLine("ok"); //Console.ReadKey(); #endregion } }
垃圾回收
class Program { static void Main(string[] args) { #region MyRegion ////int n = 100; //M1(); //Person p = new Person(); //p.Age = 19; //p.Name = "张三"; //p = null; ////GC.Collect(); ////Person p1 = p; ////p = null; ////p1 = new Person(); ////Person[] pp = new Person[10]; ////pp[0] = p; ////p = null; ////p[0].Name ////手动调用垃圾回收。 ////GC.Collect(); ////......... ////.... ////......... #endregion #region 垃圾回收 机制,代。一共有3代 Console.WriteLine(GC.MaxGeneration); //GC.Collect(); #endregion Console.WriteLine("ok"); Console.ReadKey(); } static void M1() { //值类型变量是不需要垃圾回收的,当执行完毕后立刻出栈就释放了。垃圾回收只回收堆中的内存资源。 int x = 100; x++; Console.WriteLine(x); Person p = new Person(); p.Age = 19; p.Name = "张三"; } } class Person { public string Name { get; set; } public int Age { get; set; } }
集合
using System; using System.Collections;//非泛型集合 using System.Collections.Generic;//泛型集合 using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Diagnostics; namespace 集合 { class Program { static void Main(string[] args) { //ArrayList arrayList = new ArrayList();//非泛型集合 //增加元素 //arrayList.Add(1); //arrayList.Add(99.9); //arrayList.Add("hello"); //Person p = new Person(); //p.Name = "张三"; //arrayList.Add(p); //arrayList.Add(false); ////在指定索引处插入一个新元素 //arrayList.Insert(0, "============"); //int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; //string[] names = new string[] { "乔丹", "科比", "韦德" }; //arrayList.AddRange(arr); //arrayList.AddRange(names); //ArrayList arrList2 = new ArrayList(); //arrList2.Add("中国"); //arrList2.Add("美国"); //arrList2.Add("韩国"); ////通过调用AddRange()把另一个数组或者集合加到当前arrayList中。 //arrayList.AddRange(arrList2); ////清空集合 //arrayList.Clear(); ////Console.WriteLine("循环遍历集合中每一个元素的内容:"); ////循环遍历元素 ////ArrayList可以通过下标来访问,原因就是ArrayList中有一个索引器 //for (int i = 0; i < arrayList.Count; i++) //{ // Console.WriteLine(arrayList[i].ToString()); //} //Console.ReadKey(); ////默认创建好一个空的ArrayList后,Count是0. ////Count表示,集合中实际元素的个数。 ////Capacity表示容量。 //Console.WriteLine(arrayList.Count + "============" + arrayList.Capacity); //Console.ReadKey(); //string[] n = new string[] { "a", "b", "c" }; //Array.Clear(n, 0, n.Length);//清空数值n 从第0个索引 ,清空n.Length个 //Console.WriteLine(n.Length);//3 //for (int i = 0; i < n.Length; i++) //{ // Console.WriteLine(n[i] + "★");//3个★ //} //Console.ReadKey(); ////RemoveAt ////集合初始化器 //ArrayList arrList = new ArrayList() { 1, 2, 3, 4, 5, 6, "aaa", false, 99.9 }; ////arrList.RemoveAt(0); ////arrList.RemoveAt(1); ////arrList.RemoveAt(2); ////for (int i = 0; i < arrList.Count; i++) ////{ //// Console.WriteLine(arrList[i]);//2,4,6, "aaa", false, 99.9 ////} ////Console.ReadKey(); ////要想清空集合,不能这么删除,因为集合的Count是可以动态改变大小的。 //for (int i = 0; i < arrList.Count; i++) //{ // arrList.RemoveAt(i); //} //for (int i = 0; i < arrList.Count; i++) //{ // Console.WriteLine(arrList[i]);//2,4,6, false //} //Console.ReadKey(); //ArrayList arrList = new ArrayList() { 1, 2, 3, 4, 5, 6, "aaa", false, 99.9, new Person() { Name = "张三" } }; ////把一个集合转换成一个数组 //object[] objs = arrList.ToArray(); ////根据元素内容来删除,内部是通过调用元素Equals()方法来实现的比较,所以只要Equals()方法返回值为true,则认为这两个值相等,值相等的,就可以被删除。 //arrList.Remove(1);//删除 //arrList.Remove(2);//删除 //arrList.Remove(3);//删除 //string a = new string(new char[] { 'a', 'a', 'a' }); //Person p1 = new Person() { Name = "张三" }; //arrList.Remove(a);//删除 //arrList.Remove(p1);//这个元素没有删除 //////Contains()方法内部判断两个值是否相等,也是通过Equals()方法来判断的。 ////if (arrList.Contains(1)) ////{ ////} //for (int i = 0; i < arrList.Count; i++) //{ // Console.WriteLine(arrList[i]); //} //Console.WriteLine(arrList.Count);//6 //Console.ReadKey(); #region ArrayList的Sort排序方法 //////ArrayList arr = new ArrayList() { 0, 8, 32, 3, 4, 2, 432, 5, 234, 54323, 875, 45 }; ////ArrayList arr = new ArrayList() { "Alice", "Wayen", "Chris", "Jerry", "Tom", "John", "Bob", "James", "Steve" }; //ArrayList arr = new ArrayList() { //new Person(){ Name="Alice Wang", Age=19}, //new Person(){ Name="Wayen Li", Age=12}, //new Person(){ Name="Chris Sun", Age=21}, //new Person(){ Name="Jerry Huang", Age=22} //}; //Console.WriteLine("排序之前:"); //for (int i = 0; i < arr.Count; i++) //{ // Console.WriteLine(((Person)arr[i]).Name); // //Console.WriteLine(arr[i]); //} ////IComparable ////升序排序 //arr.Sort(); //////反转 ////arr.Reverse(); //Console.WriteLine("排序之后:"); //for (int i = 0; i < arr.Count; i++) //{ // Console.WriteLine(((Person)arr[i]).Name); // //Console.WriteLine(arr[i]); //} //Console.ReadKey(); #endregion #region 通过编写不同的比较器,实现ArrayList的Sort()方法的不同方式排序 ArrayList arr = new ArrayList() { new Person(){ Name="Alice Wang", Age=19}, new Person(){ Name="Wayen Li", Age=12}, new Person(){ Name="Chris Sun", Age=21}, new Person(){ Name="Jerry Huang", Age=22} }; //按照年龄升序排序 //arr.Sort(new PersonSortByAgeAsc()); //按照年龄降序排序 //arr.Sort(new PersonSortByAgeDesc()); //按照姓名的长度升序排序 //arr.Sort(new PersonSortByNameLengthAsc()); //按照姓名长度降序排序 arr.Sort(new PersonSortByNameLengthDesc()); for (int i = 0; i < arr.Count; i++) { Console.WriteLine(((Person)arr[i]).Name); } Console.ReadKey(); #endregion } class Person : IComparable { public string Name { get; set; } public int Age { get; set; } //public override bool Equals(object obj) //{ // Person pp = obj as Person; // if (pp != null && pp.Name == this.Name) // { // return true; // } // else // { // return false; // } //} #region IComparable 成员 public int CompareTo(object obj) { Person p = obj as Person; if (p == null) { throw new ArgumentException(); } else { //return p.Age - this.Age; return this.Name.Length - p.Name.Length; } } #endregion } /// <summary> ///按照姓名长度降序排序 /// </summary> class PersonSortByNameLengthDesc : IComparer { #region IComparer 成员 public int Compare(object x, object y) { Person p1 = x as Person; Person p2 = y as Person; if (p1 != null && p2 != null) { return p2.Name.Length - p1.Name.Length; } else { throw new ArgumentException(); } } #endregion } /// <summary> /// 按照姓名长度升序排序 /// </summary> class PersonSortByNameLengthAsc : IComparer { #region IComparer 成员 public int Compare(object x, object y) { Person p1 = x as Person; Person p2 = y as Person; if (p1 != null && p2 != null) { return p1.Name.Length - p2.Name.Length; } else { throw new ArgumentException(); } } #endregion } /// <summary> /// 这个类就是一个比较器,这个比较器是一个按年龄进行升序排序的比较器 /// </summary> class PersonSortByAgeAsc : IComparer { #region IComparer 成员 public int Compare(object x, object y) { Person p1 = x as Person; Person p2 = y as Person; if (p1 != null && p2 != null) { return p1.Age - p2.Age; } else { throw new ArgumentException(); } } #endregion } /// <summary> /// 按照年龄降序排序的比较器 /// </summary> class PersonSortByAgeDesc : IComparer { #region IComparer 成员 public int Compare(object x, object y) { Person p1 = x as Person; Person p2 = y as Person; if (p1 != null && p2 != null) { return p2.Age - p1.Age; } else { throw new ArgumentException(); } } #endregion } } }
键值对集合使用,Hashtable
class Program { static void Main(string[] args) { #region 键值对集合介绍 //int[] nums = new int[] { 23, 123, 223, 323, 425, 567 }; ////1.要查找该数组中是否包含323这个数字 //int number = 323; ////////方法1: ////for (int i = 0; i < nums.Length; i++) ////{ //// if (nums[i] == number) //// { //// Console.WriteLine("包含!"); //// break; //// } ////} ////Console.ReadKey(); //////方法2: //int index = number / 100; //if (index < nums.Length) //{ // if (nums[index] == number) // { // Console.WriteLine("包含!"); // } // else // { // Console.WriteLine("不包含!"); // } //} //else //{ // Console.WriteLine("不包含!"); //} //Console.ReadKey(); #endregion #region 键值对集合使用,Hashtable //1.创建一个集合对象 Hashtable hash = new Hashtable(); //增加内容 hash.Add("ylp", "闫刘盘"); hash.Add("lsx", "刘尚鑫"); hash.Add("ljj", "李晶晶"); hash.Add("cc", "陈超"); hash.Add("bsy", "鲍守营"); hash.Add("pll", "彭莉莉"); ////循环遍历键值对集合中的每个元素 //for (int i = 0; i < hash.Count; i++) //{ // //键值对集合不能使用for循环遍历,因为无法根据索引获取内容,只能根据键来获取内容。 // Console.WriteLine(hash[i]); //} ////通过foreach循环来遍历 ////遍历键值对集合的所有的键 //foreach (object item in hash.Keys) //{ // Console.WriteLine(item.ToString()); //} ////遍历所有的值 //foreach (object item in hash.Values) //{ // Console.WriteLine(item);//遍历所有的值 //} //直接遍历 //foreach (DictionaryEntry item in hash) //{ // //直接遍历键值对 // Console.WriteLine(item.Key + " " + item.Value); //} //Console.ReadKey(); ////键值对集合的一个特点:键不能重复。 ////hash.Add("ylp","有老婆"); ////判断集合中是否已经存在某个键了 //if (hash.ContainsKey("ylp")) //{ // Console.WriteLine("已经存在ylp了。"); //} //else //{ // hash.Add("ylp", "燕刘盼"); //} ////根据键获取值 Console.WriteLine(hash["ylp"]); Console.WriteLine(hash["lsx"]); //修改 //hash["key"] = "修改"; //根据键删除某个元素 //hash.Remove("key"); Console.ReadKey(); #endregion } }
集合练习
class Program { static void Main(string[] args) { #region 案例:两个(ArrayList)集合{ “a”,“b”,“c”,“d”,“e”}和{ “d”, “e”, “f”, “g”, “h” },把这两个集合去除重复项合并成一个。 //ArrayList arr1 = new ArrayList() { "a", "b", "c", "d", "e", "e" }; //ArrayList arr2 = new ArrayList() { "d", "e", "f", "g", "h" }; //ArrayList arr3 = new ArrayList(); ////arr3.Add(arr1); //arr3.AddRange(arr1); //for (int i = 0; i < arr2.Count; i++) //{ // if (!arr3.Contains(arr2[i])) // { // arr3.Add(arr2[i]); // } //} //for (int i = 0; i < arr3.Count; i++) //{ // Console.WriteLine(arr3[i]); //} //Console.ReadKey(); #endregion #region 案例:随机生成10个1-100之间的数放到ArrayList中,要求这10个数不能重复,并且都是偶数(添加10次,可能循环很多次。) //Random random = new Random(); //ArrayList list = new ArrayList(); //while (list.Count < 10) //{ // int n = random.Next(1, 101); //[1, 101) // if (n % 2 == 0 && !list.Contains(n)) // { // list.Add(n); // } //} //for (int i = 0; i < list.Count; i++) //{ // Console.WriteLine(list[i]); //} //Console.ReadKey(); #endregion #region 练习:有一个字符串是用空格分隔的一系列整数,写一个程序把其中的整数做如下重新排列打印出来:奇数显示在左侧、偶数显示在右侧。比如”2 7 8 3 22 9 5 11”显示成”7 3 9 2 8 22….”。 //string strs = "2 7 8 3 22 9 5 11"; //string[] nums = strs.Split(' '); ////奇数 //ArrayList arrOdd = new ArrayList(); ////偶数 //ArrayList arrEven = new ArrayList(); ////循环nums数组 //for (int i = 0; i < nums.Length; i++) //{ // if (Convert.ToInt32(nums[i]) % 2 == 0) // { // arrEven.Add(nums[i]); // } // else // { // arrOdd.Add(nums[i]); // } //} ////把奇数集合与偶数集合合并到一起 //arrOdd.AddRange(arrEven); ////拼接字符串 //StringBuilder sb = new StringBuilder(); //for (int i = 0; i < arrOdd.Count; i++) //{ // sb.Append(arrOdd[i] + " "); //} //Console.WriteLine(sb.ToString().Trim()); //Console.ReadKey(); #endregion } }
在C#中数组,ArrayList,List都能够存储一组对象,那么这三者到底有什么样的区别呢。
数组
数组在C#中最早出现的。在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单。
但是数组存在一些不足的地方。在数组的两个数据间插入数据是很麻烦的,而且在声明数组的时候必须指定数组的长度,数组的长度过长,会造成内存浪费,过段会造成数据溢出的错误。如果在声明数组时我们不清楚数组的长度,就会变得很麻烦。
针对数组的这些缺点,C#中最先提供了ArrayList对象来克服这些缺点。
ArrayList
ArrayList是命名空间System.Collections下的一部分,在使用该类时必须进行引用,同时继承了IList接口,提供了数据存储和检索。ArrayList对象的大小是按照其中存储的数据来动态扩充与收缩的。所以,在声明ArrayList对象时并不需要指定它的长度。
<span style="font-family:SimSun;font-size:18px;">//ArrayList ArrayList list1 = new ArrayList(); //新增数据 list1.Add("cde"); list1.Add(5678); //修改数据 list[2] = 34; //移除数据 list.RemoveAt(0); //插入数据 list.Insert(0, "qwe"); </span>
从上面例子看,ArrayList好像是解决了数组中所有的缺点,为什么又会有List?
我们从上面的例子看,在List中,我们不仅插入了字符串cde,而且插入了数字5678。这样在ArrayList中插入不同类型的数据是允许的。因为ArrayList会把所有插入其中的数据当作为object类型来处理,在我们使用ArrayList处理数据时,很可能会报类型不匹配的错误,也就是ArrayList不是类型安全的。在存储或检索值类型时通常发生装箱和取消装箱操作,带来很大的性能耗损。
装箱与拆箱的概念:
简单的说:
装箱:就是将值类型的数据打包到引用类型的实例中
比如将string类型的值abc赋给object对象obj
<span style="font-family:SimSun;font-size:18px;">String i=”abc”; object obj=(object)i; </span>
拆箱:就是从引用数据中提取值类型
比如将object对象obj的值赋给string类型的变量i
<span style="font-family:SimSun;font-size:18px;">object obj=”abc”; string i=(string)obj; </span>
装箱与拆箱的过程是很损耗性能的。
泛型List
因为ArrayList存在不安全类型与装箱拆箱的缺点,所以出现了泛型的概念。List类是ArrayList类的泛型等效类,它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。
比如:
上例中,如果我们往List集合中插入int数组123,IDE就会报错,且不能通过编译。这样就避免了前面讲的类型安全问题与装箱拆箱的性能问题了。
总结:
数组的容量是固定的,您只能一次获取或设置一个元素的值,而ArrayList或List<T>的容量可根据需要自动扩充、修改、删除或插入数据。
数组可以具有多个维度,而 ArrayList或 List< T> 始终只具有一个维度。但是,您可以轻松创建数组列表或列表的列表。特定类型(Object 除外)的数组 的性能优于 ArrayList的性能。 这是因为 ArrayList的元素属于 Object 类型;所以在存储或检索值类型时通常发生装箱和取消装箱操作。不过,在不需要重新分配时(即最初的容量十分接近列表的最大容量),List< T> 的性能与同类型的数组十分相近。
在决定使用 List<T> 还是使用ArrayList 类(两者具有类似的功能)时,记住List<T> 类在大多数情况下执行得更好并且是类型安全的。如果对List< T> 类的类型T 使用引用类型,则两个类的行为是完全相同的。但是,如果对类型T使用值类型,则需要考虑实现和装箱问题。