关于string,我今天科普的
今天下午朋友讨论组上讨论一个关于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,我今天科普的的更多相关文章
- 科普:String hashCode 方法为什么选择数字31作为乘子
		1. 背景 某天,我在写代码的时候,无意中点开了 String hashCode 方法.然后大致看了一下 hashCode 的实现,发现并不是很复杂.但是我从源码中发现了一个奇怪的数字,也就是本文的主 ... 
- 科普:为什么 String hashCode 方法选择数字31作为乘子
		作者:coolblog 此文章转载自:https://segmentfault.com/a/1190000010799123 1. 背景 某天,我在写代码的时候,无意中点开了 String hashC ... 
- Lua的string和string库总结
		Lua有7种数据类型,分别是nil.boolean.number.string.table.function.userdata.这里我总结一下Lua的string类型和string库,复习一下,以便加 ... 
- (letcode)String to Integer (atoi)
		Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input cases. ... 
- 【转】科普Spark,Spark是什么,如何使用Spark
		本博文是转自如下链接,为了方便自己查阅学习和他人交流.感谢原博主的提供! http://www.aboutyun.com/thread-6849-1-1.html http://www.aboutyu ... 
- repeater绑定泛型list<string>
		菜鸟D重出江湖,依然是菜鸟,囧!言归正传—— 工作中遇到一个repeater绑定的问题,数据源是一个list<string> 集合,然后在界面上使用<%#Eval()%>绑定. ... 
- java基础(五)  String性质深入解析
		引言 本文将讲解String的几个性质. 一.String的不可变性 对于初学者来说,很容易误认为String对象是可以改变的,特别是+链接时,对象似乎真的改变了.然而,String对象一经创 ... 
- String hashCode 方法为什么选择数字31作为乘子
		1. 背景 某天,我在写代码的时候,无意中点开了 String hashCode 方法.然后大致看了一下 hashCode 的实现,发现并不是很复杂.但是我从源码中发现了一个奇怪的数字,也就是本文的主 ... 
- JDK源码学习笔记——String
		1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() { ... 
随机推荐
- web前端页面项目经验总结
			项目时间:2016年4月5日--4月9日项目名称:阿七果子园web前端页面项目内容: 1.HTML5+CSS+JavaScript(banner+timer)+JQuery(small_bann ... 
- django orm总结[转载]
			django orm总结[转载] 转载地址: http://www.cnblogs.com/linjiqin/archive/2014/07/01/3817954.html 目录1.1.1 生成查询1 ... 
- 时间的处理  --java
			得到当天时间 SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd" ); String todayData = ... 
- 关于MVC4项目从32位机移到64位机编译报错解决方案
			早上写了个MVC WEBAPI的demo 机子环境是:XP SP3+ VS2010 + ASP.NET MVC4 然后晚上拿回笔记本进行编译运行,结果报错如下: “/”应用程序中的服务器错误. 分析器 ... 
- Ganglia安装搭建
			Ganglia的安装部署 前言 1 一.Ganglia组件 1 二.安装依赖 2 三.安装expat依赖 2 四.安装confuse 3 五.安装ganglia 4 六. 服务端配置(gmetad 节 ... 
- 【图像处理】【SEED-VPM】1.板子基本操作流程
			>>>>>>>>>>>>>>>>>>>>>>>>> ... 
- MYSQL数据库忘记密码
			1.忘记密码解决办法 Windows下的实际操作如下 1.关闭正在运行的MySQL. 2.打开DOS窗口,转到mysql\bin目录. 3.输入mysqld --skip-grant-tables回车 ... 
- Android的权限检查
			Application的权限: 可以在AndroidManifest.xml中用<permission>定义运行Application需要的权限. 用<uses-permission ... 
- Apriori on MapReduce
			Apiroi算法在Hadoop MapReduce上的实现 输入格式: 一行为一个Bucket 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 34 36 38 ... 
- number 数据类型的分析。
			在js中,number数据类型可能算最令人关注的的类型之一了. number类型分为整数和浮点数. 一,整型数,整型又分为十进制,八进制,十六进制. 十进制即是生活中接触到的:而八进制数的首位必须是零 ... 
