欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

聊聊字符串拼接的哪一些事儿

程序员文章站 2022-06-29 10:46:38
字符串对我编程人员来说是字符串时每天见面的常客,你不认识不熟悉他都不得行,字符串的拼接更是家常便饭,那么在实际开发过程中实现字符串的拼接有哪一些方式呢?咋们一起来聊聊,来交流沟通,学习一波。也许你会说,那也太简单了嘛,哈哈,那可不一定 ......

​        字符串对我编程人员来说是字符串时每天见面的常客,你不认识不熟悉他都不得行,字符串的拼接更是家常便饭,那么在实际开发过程中实现字符串的拼接有哪一些方式呢?咱们一起来聊聊,来交流沟通,学习一波。也许你会说,那也太简单了嘛,谁不会啊,哈哈,使用起来确实简单,但是不一定我们都使用的方式还有优秀的方式吗?

    在文章前,我们先简单聊聊关于string的数据类型存储必须了解概念:

    string是一个引用类型,是一个sealed类,存储在堆内存上,每一次修改都会从新创建一个新的string来存储,原始的会自动被回收。这个是不感觉是废话,人人都知道嘛,哈哈哈。

    下面以c#为开发语言来说明:实现字符串的拼接常用的方式有如下四种

 

其一、直接通过+拼接

 

    直接通过+拼接是我们在代码中最常见的一种方式,下面以一个简单的代码段来分析分析

    string str="1";

    str=str+"2";

    第一段代码,首先分配了一个内存空间来存储str变量,其值为“1”

    第二段代码,重新分配了一个新的内存空间来存储“12”,并将str指向新地址

    通过分析,其实我们不难发现,两端就简单的代码,就会有两次内存地址操作,随着拼接字符串的个数地址,分配内存地址的次数也递增,当几个简单的字符串通过该方式拼接时,其实我们还是感觉不到性能的影响,但是当字符串数量大时,你都会有感觉了,那样不仅仅造成内存的浪费,还直接影响性能。

所以在实际开发工程中,通过+拼接字符串比较常见,但是如果只是见到这种方式也就不那么友好了,既然不友好,那么显然就会有比较友好的方式啦,下面我们就分析分析通过stringbuilder来实现字符串的拼接。

 

其二、通过stringbuilder拼接字符串

 

    stringbuilder其实内部相当于是维护的一个字符数组,是一个可以动态增加自身数据长度,其默认长度为16,当存储的字符串超出其长度是,会自动扩容2倍长度。

    哈哈,说到这儿,估计你看出了问题,那就是超出长度自动扩容,自动扩容是不是也需要牺牲性能,当然在几次扩容你还感觉不到性能的影响,但是如果词数多了,你就会感觉很明显,这也是对stringbuilder的一些使用技巧。

我们去看不同小伙伴的代码,你就会发现,技术老鸟,在初始化stringbuilder的时候会根据预估将要存储的字符串大小,给stringbuilder初始化一个长度,这也就是细节上的差距体现。

    说了半天的废话,是不是要来的实际的代码来证明说的不是废话呢?不急不急,在文章最后,我会专门写测试代码对比分析的。

 

其三、string.format不陌生吧

 

    对于一些格式的数据拼接填充,string.format也是经常看见的,他的一个很大好处就是,看上去比较清晰

    其实我们看过string的底层实现我们会发现,其底层本质还是stringbuilder来实现的

    下面就是string.format的源码实现

public static string format(iformatprovider provider, string format, params object[] args) <br>{

    if (format == null || args == null)

      throw new argumentnullexception((format==null)?"format":"args");

    stringbuilder sb = new stringbuilder(format.length + args.length * 8);

    sb.appendformat(provider,format,args);

    return sb.tostring();

}

 

    其实string.format使用起来很简单,我就不在啰嗦介绍了,免得大家觉得烦,哈哈哈

    string result=string.format("大家好,我叫{0},今年{1}","程序员修炼之旅",1);

 

其四、$方式拼接字符串

 

    c#6.0出现了$方式拼接字符串,其实简单说就是string.format简化操作版,string.format如果拼接的字符串太多,估计自己都懵逼的分不清对应关系了,不知道你们遇到过没有,反正我原来是遇到过的。$就很好的规避了该问题,那么下    面来一个例子说明一切:

    

string name = "程序员修炼之旅";​    
int age = 1;​    
string str = string.format("my name is{0}, i'm {1} years old",name,age);​    
string str2 = $"my name is{name}, i'm {age} years old";​    
最终结果是:str=str1

其五,当然还有其他方式,不在此啰嗦了,后续在讨论

 

测试分析

    说了半天,不拿点实际东西来测试,我知道你是不会信服的,下面就直接上测试代码:

using system;
using system.diagnostics;
using system.text;

namespace stringsplicingtest
{
    /// <summary>
    /// 字符串拼接练习
    /// </summary>
    public class program
    {
        /// <summary>
        /// 主函数入口
        /// </summary>
        /// <param name="args"></param>
        static void main(string[] args)
        {
            // 测试分别通过+ 和 stringbuilder 来连接 0 之100的数字
            console.writeline("测试分别通过+ 和 stringbuilder 来连接");
            console.writeline("");
            console.writeline("测试连接 0 - 100 的数字");
            console.writeline("");
            plusstring(100);
            stringbuilderstring2(100);
            console.writeline("");

            console.writeline("");
            console.writeline("测试连接 0 - 10000 的数字");
            plusstring(10000);
            stringbuilderstring2(10000);
            console.writeline("");
            console.writeline("");

            // 下面测试一下同样是stringbuilder连接字符串,一个是定义吃指定长度,一个是不指定长度对比
            console.writeline(@"下面测试一下同样是stringbuilder连接字符串, 一个是定义并指定长度,一个是不指定长度对比");
            console.writeline("");
            console.writeline("测试连接 0 - 1000000 的数字");
            console.writeline("不初始化长度");
            stringbuilderstring(1000000);
            console.writeline("初始化长度");
            stringbuilderstring2(1000000);

            console.writeline("");
            console.writeline("");

            console.writeline("测试连接 0 - 10000000 的数字");
            console.writeline("不初始化长度");
            stringbuilderstring(10000000);
            console.writeline("初始化长度");
            stringbuilderstring2(10000000);

            console.readline();
        }

        /// <summary>
        /// 通过+拼接字符串
        /// </summary>
        /// <param name="totalnum"></param>
        private static void plusstring(int totalnum)
        {
            //// 定义一个秒表,执行获取执行时间
            stopwatch st = new stopwatch();//实例化类
            st.start();//开始计时

            console.writeline("开始执行,通过+连接字符串:");
            string result = "";
            //// 定义一个数组
            for (int i = 0; i < totalnum; i++)
            {
                result = result + i.tostring();
            }

            //需要统计时间的代码段

            st.stop();//终止计时
            console.writeline(string.format("执行完毕,通过+连接字符串!总耗时{0}毫秒",
            st.elapsedmilliseconds.tostring()));
        }

        /// <summary>
        /// 通过s拼接字符串
        /// </summary>
        /// <param name="totalnum"></param>
        private static void stringbuilderstring(int totalnum)
        {
            //// 定义一个秒表,执行获取执行时间
            stopwatch st = new stopwatch();//实例化类
            st.start();//开始计时

            console.writeline("开始执行,通过 stringbuilder 连接字符串:");

            stringbuilder result = new stringbuilder();

            //// 定义一个数组
            for (int i = 0; i < totalnum; i++)
            {
                result.append(i.tostring());
            }

            string result2 = result.tostring();
            //需要统计时间的代码段

            st.stop();//终止计时
            console.writeline(string.format("执行完毕,通过 stringbuilder 连接字符串!总耗时{0}毫秒",
            st.elapsedmilliseconds.tostring()));
        }


        /// <summary>
        /// 通过stringbuilder拼接字符串,初始化时指定一个长度
        /// </summary>
        /// <param name="totalnum"></param>
        private static void stringbuilderstring2(int totalnum)
        {
            //// 定义一个秒表,执行获取执行时间
            stopwatch st = new stopwatch();//实例化类
            st.start();//开始计时

            console.writeline("开始执行,通过 stringbuilder 连接字符串:");

            stringbuilder result = new stringbuilder(totalnum * 6);

            //// 定义一个数组
            for (int i = 0; i < totalnum; i++)
            {
                result.append(i.tostring());
            }

            string result2 = result.tostring();
            //需要统计时间的代码段

            st.stop();//终止计时
            console.writeline(string.format("执行完毕,通过 stringbuilder 连接字符串!总耗时{0}毫秒",
            st.elapsedmilliseconds.tostring()));
        }
    }
}

 


聊聊字符串拼接的哪一些事儿

 

结果分析总结:

测试分两个点:

其一测试的是:通过+和stringbuilder拼接字符串的性能比较哦

其二测试的是:stringbuilder初始化长度和不初始化长度的性能比较

大概得出以下几点结论

1、在待拼接的字符串少的时,+和stringbuilder没有明显的性能差距

2、当拼接的字符串多时,stringbuilder的优势越来越明显

3、同样是stringbuilder拼接字符串,预估初始化长度的效率比不初始化指定长度的效率高

说到此,我相信大家都知道该怎么使用了。好了,时间不早了,赶紧洗洗睡了,明天还得上班呢?

 end

 

 
欢迎各位小伙伴关注我的公众号(程序员修炼之旅),里面会分享一些技术类干货,同时也是一个技术沟通交流平台,谢谢大家的支持。

聊聊字符串拼接的哪一些事儿