今天下午朋友讨论组上讨论一个关于string的问题,问题是这样的,string a="aaa";string b=a;a="bbb",为什么测试b的值不改变?之前我看过一个文章,知道肯定不相等,因为引用地址的一系列问题,但是不能很好的解释于同事听,所以几经查阅资料,在博客园里找到一篇文章,解决了我的疑问,同时也解决了关于C#中"=="与equals的计算结果与别的语言不一致的问题。在此转载过来,以备巩固。(以下为转载内容,转载地址http://terrylee.cnblogs.com/archive/2005/12/26/304876.html
概述

String在任何语言中,都有它的特殊性,在.NET中也是如此。它属于基本数据类型,也是基本数据类型中唯一的引用类型。字符串可以声明为常量,但是它却放在了堆中。希望通过本文能够使大家对.NET中的String有一个深入的了解。

不可改变对象

在.NET中String是不可改变对象,一旦创建了一个String对象并为它赋值,它就不可能再改变,也就是你不可能改变一个字符串的值。这句话初听起来似乎有些不可思议,大家也许马上会想到字符串的连接操作,我们不也可以改变字符串吗?看下面这段代码:

using System;

namespace Demo1
{
/// <summary>
/// String连接测试
/// </summary>
public class Test
{
public static void Main(string[] args)
{
string a = "";
Console.WriteLine(a); a += "";
Console.WriteLine(a);
Console.ReadLine();
}
}
}

运行的结果:

1234

12345678

看起来我们似乎已经把MyStr的值从“1234”改为了“12345678”。事实是这样的吗?实际上并没有改变。在第5行代码中创建了一个String对象它的值是“1234”,MyStr指向了它在内存中的地址;第七行代码中创建了一个新的String对象它的值是“12345678”,MyStr指向了新的内存地址。这时在堆中其实存在着两个字符串对象,尽管我们只引用了它们中的一个,但是字符串“1234”仍然在内存中驻留。

引用类型

前面说过String是引用类型,这就是如果我们创建很多个相同值的字符串对象,它在内存中的指向地址应该是一样的。也就是说,当我们创建了字符串对象a,它的值是“1234”,当我们再创建一个值为“1234”的字符串对象b时它不会再去分配一块内存空间,而是直接指向了a在内存中的地址。这样可以确保内存的有效利用。看下面的代码:

using System;

namespace Demo2
{
/// <summary>
/// String引用类型测试
/// </summary>
public class Test
{
public static void Main(string[] args)
{
string a = ""; Console.WriteLine(a); Test.Change(a); Console.WriteLine(a);
Console.ReadLine();
} public static void Change(string s)
{
s = "";
}
}
}

运行结果:

1234

1234

做一个小改动,注意Change(ref string s)

using System;

namespace Demo2
{
/// <summary>
/// String引用类型测试
/// </summary>
public class Test
{
public static void Main(string[] args)
{
string a = ""; Console.WriteLine(a); Test.Change(ref a); Console.WriteLine(a);
Console.ReadLine();
} public static void Change(ref string s)
{
s = "";
}
}
}

运行结果:

1234

5678

字符串的比较

在.NET中,对字符串的比较操作并不仅仅是简单的比较二者的值,= =操作首先比较两个字符串的引用,如果引用相同,就直接返回True;如果不同再去比较它们的值。所以如果两个值相同的字符串的比较相对于引用相同的字符串的比较要慢,中间多了一步判断引用是否相同。看下面这段代码:

using System;

namespace Demo3
{
/// <summary>
/// String类型的比较
/// </summary>
public class Test
{
public static void Main(string[] args)
{
string a = "";
string b = "";
string c = "";
c += ""; int times = ;
int start,end; ///测试引用相同所用的实际时间
start = Environment.TickCount;
for(int i=;i<times;i++)
{
if(a==b)
{}
}
end = Environment.TickCount;
Console.WriteLine((end-start)); ///测试引用不同而值相同所用的实际时间
start = Environment.TickCount;
for(int i=;i<times;i++)
{
if(a==c)
{}
}
end = Environment.TickCount;
Console.WriteLine((end-start)); Console.ReadLine();
}
}
}

执行的结果(运行的结果可能有些不同):

1671

4172
由此我们看出值相同时的比较用= =比引用相同时的比较慢了好多。这里仅仅是一个测试,因为做这样的比较并没有任何实际的意义。
有一点需要明确的是,.NET中==跟Equals()内部机制完全是一样的,==是它的一个重载。

public static bool operator ==(string a, string b)
{
return string.Equals(a, b);
} public static bool Equals(string a, string b)
{
if (a == b)
{
return true;
}
if ((a != null) && (b != null))
{
return a.Equals(b);
}
return false;
}

字符串驻留

看一下这段代码:

using System;

namespace Demo4
{
/// <summary>
/// String的驻留
/// </summary>
public class Test
{
public static void Main(string[] args)
{
string a = "";
string s = "";
s += ""; string b = s;
string c = String.Intern(s); Console.WriteLine((object)a == (object)b);
Console.WriteLine((object)a == (object)c);
Console.ReadLine();
}
}
}

执行的结果:

False

True
在这段代码中,比较这两个对象发现它的引用并不是一样的。如果要想是它们的引用相同,可以用Intern()函数来进行字符串的驻留(如果有这样的值存在)。

StringBuilder对象

通过上面的分析可以看出,String类型在做字符串的连接操作时,效率是相当低的,并且由于每做一个连接操作,都会在内存中创建一个新的对象,占用了大量的内存空间。这样就引出StringBuilder对象,StringBuilder对象在做字符串连接操作时是在原来的字符串上进行修改,改善了性能。这一点我们平时使用中也许都知道,连接操作频繁的时候,使用StringBuilder对象。但是这两者之间的差别到底有多大呢?来做一个测试:

using System;
using System.Text; namespace Demo5
{
/// <summary>
/// String和StringBulider比较
/// </summary>
public class Test
{
public static void Main(string[] args)
{
string a = "";
StringBuilder s = new StringBuilder(); int times = ;
int start,end; ///测试String所用的时间
start = Environment.TickCount;
for(int i=;i<times;i++)
{
a += i.ToString();
}
end = Environment.TickCount;
Console.WriteLine((end-start)); ///测试StringBuilder所用的时间
start = Environment.TickCount;
for(int i=;i<times;i++)
{
s.Append(i.ToString());
}
end = Environment.TickCount;
Console.WriteLine((end-start)); Console.ReadLine();
}
}
}

运行结果:

884

0

通过上面的分析,可以看出用String来做字符串的连接时效率非常低,但并不是所任何情况下都要用StringBuilder,当我们连接很少的字符串时可以用String,但当做大量的或频繁的字符串连接操作时,就一定要用StringBuilder。

关于string,我今天科普的的更多相关文章

  1. 科普:String hashCode 方法为什么选择数字31作为乘子

    1. 背景 某天,我在写代码的时候,无意中点开了 String hashCode 方法.然后大致看了一下 hashCode 的实现,发现并不是很复杂.但是我从源码中发现了一个奇怪的数字,也就是本文的主 ...

  2. 科普:为什么 String hashCode 方法选择数字31作为乘子

    作者:coolblog 此文章转载自:https://segmentfault.com/a/1190000010799123 1. 背景 某天,我在写代码的时候,无意中点开了 String hashC ...

  3. Lua的string和string库总结

    Lua有7种数据类型,分别是nil.boolean.number.string.table.function.userdata.这里我总结一下Lua的string类型和string库,复习一下,以便加 ...

  4. (letcode)String to Integer (atoi)

    Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input cases. ...

  5. 【转】科普Spark,Spark是什么,如何使用Spark

    本博文是转自如下链接,为了方便自己查阅学习和他人交流.感谢原博主的提供! http://www.aboutyun.com/thread-6849-1-1.html http://www.aboutyu ...

  6. repeater绑定泛型list<string>

    菜鸟D重出江湖,依然是菜鸟,囧!言归正传—— 工作中遇到一个repeater绑定的问题,数据源是一个list<string> 集合,然后在界面上使用<%#Eval()%>绑定. ...

  7. java基础(五) String性质深入解析

    引言   本文将讲解String的几个性质. 一.String的不可变性   对于初学者来说,很容易误认为String对象是可以改变的,特别是+链接时,对象似乎真的改变了.然而,String对象一经创 ...

  8. String hashCode 方法为什么选择数字31作为乘子

    1. 背景 某天,我在写代码的时候,无意中点开了 String hashCode 方法.然后大致看了一下 hashCode 的实现,发现并不是很复杂.但是我从源码中发现了一个奇怪的数字,也就是本文的主 ...

  9. JDK源码学习笔记——String

    1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() { ...

随机推荐

  1. ionic 通过PouchDB + SQLite来实现app的本地存储(Local Storage)

    首先声明,本教程参考国外网站(http://gonehybrid.com/how-to-use-pouchdb-sqlite-for-local-storage-in-your-ionic-app/) ...

  2. Linux mount的使用

    大家在使用Linux系统的时候,肯定用过一些共享文件的东西,比如FTP,Mount 等等,接下来我重点说一下Mount的用法: 现在有一台测试环境上面需要部署Mount服务器(10.10.10.27) ...

  3. windows php swoole 安装

    Cygwin 官方地址:http://www.cygwin.com/ swoole 官方下载地址:https://github.com/swoole/swoole-src/releases 1.下载 ...

  4. 导航效果css

    <!doctype html> <html> <head> <meta charset="utf-8" /> <style&g ...

  5. VS2012+LUA环境搭建

    1 .启动VS2012,选择C++下的"win32"项目类型中的"Win2控制台应用程序" 2.工具——选项——项目和解决方案——VC++目录——可执行程序(C ...

  6. (转)Oracle 在Drop表时的Cascade Constraints

    Oracle 在Drop表时的Cascade Constraints 假设A为主表(既含有某一主键的表),B为从表(即引用了A的主键作为外键).则当删除A表时,如不特殊说明,则 drop table  ...

  7. 一般处理程序获取WEB窗体创建的验证码需要实现session相关接口

    如下: using System.Web.SessionState; using ASPNETAJAXWeb.ValidateCode.Page; public class CheckLogin : ...

  8. hongxin

    邀请链接 :http://honx.in/_VbiG2CZDjwIE8l1t

  9. C语言学习1——结构体剖析

    一、定义结构体变量的方法 1.1先声明结构体类型在定义变量名 例如: a.声明结构体类型 struct student { int num; char name[20]; char sex; int ...

  10. Y+的查看及FLUENT壁面函数的选择

    y+的查看 其实,我们关心的应该是壁面y+值.那么我们看云图的话,是可以直接看到的,但是个人感觉,如果case大的话,也不是很方便.此外,你要是看云图的话,要用filled的方式,而且把node va ...