• c#基础知识


    1.string与StringBuilder

       string 是不可变的,是引用类型继承与Object(值类型继承于ValueType),每次拼接string其实是在托管堆上构造一个新的对象。这样在反复的拼接字符串的时候就会产生大量的垃圾字符串,由GC自动回收,这个时候GC会频繁的回收垃圾字符串,影响性能,所以通常推荐使用StringBuilder来做字符串拼接,

            那么string支持字符串拼接的意义何在呢?

             这里就得说到字符串池这个概念,当我们创建一个字符串的时候,会先去字符串池中找(哈希表),如果没有找到就会创建一个新的字符串并保存到字符串池中,当创建的字符串已经在字符串池中存在的时候,就会去引用。这样已经创建过的字符串就可以共享,节省空间。因为在程序中,我们会使用大量的字符串,假如我们每次出现的字符串都在内存空间中开辟一块空间来存储,就会在内存中产生大量相同的字符串,也会造成垃圾回收频繁的执行,影响性能。所以string的字符串拼接适合少量的拼接,对于大量的拼接,则推荐使用StringBuilder。

          下面说说string的字符串拼接与StringBuilder拼接的简单过程。

     string的字符串拼接过程:string a='A'; a+=B;

    1.开辟足够大的临时存储空间来保证足够存储字符串A和B。

    2.将A复制到临时区的开始处。

    3.将B复制到临时存储区的结尾处。

    4.释放a的旧有内存。

    5.给a分配新内存。

    6.将临时存储空间的字符串复制到5中新开辟的内存,并将a的引用指向新内存。

      StringBuilder拼接的过程:StringBuilder是链式存储结构,构造函数初始化StringBuilder实例的大小,可存储的最大容量,当前字符串。当新添加的字符串大于当前StringBuilder剩余空间的时候,StringBuilder就会开辟出一块新的空间(大小=原大小*2),新字符串补充满剩余内存空间后,将剩下的字符串放入新开辟的内存空间。引用一下网上的一张图。

     2者之间的本质区别在于,string在字符串拼接的时候是创建一个新的对象来保存拼接后的字符串,而StringBuilder则是在原StringBuilder对象上开辟新的内存空间,是操作原对象而非string的创建新对象。

    2.值类型一定在栈上吗?

      我们都知道值类型是存放在线程栈上面,但其实只有值类型作为临时变量的时候才存放在栈上,作为成员变量,存储在堆中。

    1.引用类型的内部变量,即使是值类型,也会在引用类型被实例是一起存放在堆上,方法内部的值类型变量不会初始化,在执行方法的时候放在栈上面。

    2.对于值类型的数组,因为数组是引用类型。所以数组内的值类型也在堆上。

    3.闭包,lamda表达式。如下:           

         Action<int> act = a =>{  Console.WriteLine(a); };

    C# 成的IL 会添加一个静态的辅助类,闭包内的局部变量 也会成为辅助类的成员变量,因此,这种值类型的局部变量也被分配到堆上。

    闭包的陷阱:参考 菩提树下的杨过 :http://www.cnblogs.com/yjmyzz/archive/2009/03/13/1410924.html

    using System;
    using System.Collections.Generic;
    
    namespace ConsoleTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                List<Action> ls = new List<Action>();
                for (int i = 0; i < 10; i++)
                {                
                    ls.Add(() => Console.WriteLine(i));                
                }
    
                foreach (Action action in ls)
                {
                    action();
                }
                System.Console.Read();
            }       
        }  
    
    }

    结果:一连输出了10行完全相同的"10"(可能并没有按代码编写者的"意图",输出0到9)

    看了链接里面其实是编译生成了一个类,类里面有一个变量i和打印i的方法,然后创建一个Action委托集合将方法赋值,最后遍历集合执行,在执行的时候i已经加到10了。所以要通过一个内部变量来避免这种陷阱。。

    3.哈希表的实现方法?

       哈希表 hashtable ,也称散列表,主要是根据关键码值(key,value)来直接访问数据结构,通过把关键码值映射到表中一个位置来访问记录。

      它的实现方法是:把key通过一个固定的算法函数(除余法,数字选择法)来转换成一个整数字符,然后将该整数除以一个数字进行取余,将余数作为数组的下标,将value存储在该余数的数组下标的空间内。当使用哈希表查询的时候,再次将key通过相同函数转换整数取余得到下标,定位到对应的数组控件获取Value值。(通过余数分配不同的数组空间,查询的时候通过余数找对应的数组空间,这样就避免了全表扫描,节约时间。)

    a) 除余法: 
           选择一个适当的正整数 p ,令 h(k ) = k mod p ,这里, p 如果选取的是比较大的素数,效果比较好。而且此法非常容易实现,因此是最常用的方法。 
    b) 数字选择法: 
           如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。 

                 

      

        

  • 相关阅读:
    bzoj1432_[ZJOI2009]Function
    Luogu1681_ 最大正方形II
    初等数论-Base-2(扩展欧几里得算法,同余,线性同余方程,(附:裴蜀定理的证明))
    [bzoj2456] mode
    初等数论-Base-1(筛法求素数,欧拉函数,欧几里得算法)
    小程序之Tab切换
    vue-axios基本用法
    vue-过渡动画
    vue-router实例
    永恒之蓝漏洞利用复现
  • 原文地址:https://www.cnblogs.com/m7777/p/4106393.html
Copyright © 2020-2023  润新知