什么,又是字符串拼接,我有些不淡定了

1、乱弹StringJoiner的横空出世

为了这种字符串拼接,不论是C#还是Java都有对应的改进的类和方法。比如我们熟知的C#下的StringBuilder类,string的format方法,还有Java中不是也有StringBuffer类吗?但是实际开发中,很多人还是喜欢直接用string加过来加过去,replace也会适时出现几次,提升自己的曝光度。虽然写代码的人很省事,可读性严格来说也不是那么的差,而且运行起来性能也不是离谱的一无是处,完事之后说不定哪一天写代码的人就收拾金银细软走人了,大大小小若干项目都是这种代码可就……碰巧后来维护这种代码的人非常争气,想到了好的解决方案,捏住鼻子含怒重构了这种低效的代码之后,发扬无私共享的风格在园子里贴了出来造福大众,一不小心还成了“拯救那些性能低下的字符串拼装代码“的先驱,CoolCode,我说的对吗?

关于StringJoiner的前世今生,请参考CoolCode的大作:

StringJoiner 拯救那些性能低下的字符串拼装代码

 

2、基本方法还可以再添加几个,命名还可以再装腔作势一点

在我的好友CoolCode童鞋的原文中,他没有把所有源码都贴出来。我在项目中使用的时候觉得还可以添加几个常用的方法,比如Replace、Remove和Clear方法等等,因为它们的使用频率也很高。同时,我们还可以考虑到将这个类“常识化”,说不定哪天大家都觉得这个好使,接着大面积推广使用代替string或者StringBuilder了,没有可能吗?所以我们还可以把string的Format静态方法,StringBuilder的Append和AppendFormat实例方法也给它弄进去。

比如Format静态方法,我们可以像如下定义:

1
2
3
4
5
6
public  static  StringBuffer Format( string  format, object  arg0)
  {
      StringBuffer sb = new  StringBuffer();
      sb.builder.AppendFormat(format, arg0);
      return  sb;
  }

而两个实例方法Append和AppendFormat,对于直接字符串拼接的重构很少用到,貌似没有必要写进去(这两个实例方法也不是毫无作为,看项目需要,可以注释掉该扩展方法,作者补充),幸好我们还有扩展方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// <summary>
/// StringBuffer的扩展
/// </summary>
public  static  class  StringBufferExtension
{
     public  static  StringBuffer Append( this  StringBuffer sb, string  input)
     {
         sb.builder.Append(input);
         //sb += input;
         return  sb;
     }
     public  static  StringBuffer Append( this  StringBuffer sb, object  input)
     {
         sb.builder.Append(input);
         //sb += input;
         return  sb;
     }
 
     public  static  StringBuffer AppendFormat( this  StringBuffer sb, string  format, params  object [] args)
     {
         sb.builder.AppendFormat(format, args);
         return  sb;
     }
 
}

关于这个StringJoiner的命名好像稍微也不是很贴近大众。上面不是提到Java中的StringBuffer类么,像这种出类拔萃的命名,除了CoolCode,谁还能割舍得下呢 ( ^_^)? 本文最后采用了StringBuffer类名,demo中可以看到,不是说StringJoiner就不好,老实说这是我见过的最本土化的命名之一。再次感谢CoolCode的无私贡献,实际项目开发和维护中这个类拯救我不是一次两次了。

 

最后,demo下载:StringBuffer

 

参考文章:

http://www.cnblogs.com/coolcode/archive/2009/10/13/StringJoiner.html

http://blog.zhaojie.me/2009/11/string-concat-perf-1-benchmark.html

http://blog.zhaojie.me/2009/12/string-concat-perf-3-profiling-analysis.html

 

附:务必小心OutOfMemory和StackOverFlow两种异常

这几天晚上我重新看<<CLR via C#>>关于异常和状态管理的章节。结合自己的经验,发现除了空引用、参数和索引越界等等常见异常之外,书中提到还应该注意到OutOfMemory和StackOverFlow两种异常,虽然这两种异常在实际项目中出现的概率微乎其微。

一、“内存不够用”

OutOfMemoryException异常,字面理解,就是超出内存额定容量而抛出的异常。为什么会超出内存额定容量呢?很简单,内存空间是有限的,但是我们分配内存的要求是无限的(好像是某名言)。

1、程序中一次性要往内存存放的数据过多

举例来说,我们每次去取数据库的数据,如果每次都取个几百万上千万的数据,普通PC通常情况下内存都不是很大(我用过的最大也就4G而已),如果取回来的数据在内存中存储的数据结构再复杂一点(比如带嵌套结构的字典、双向链表等等),在取回数据进行内存分配的时候,第一次分配就挂了,CLR二话不说就抛出了OutOfMemoryException异常。

2、或者表面上看上去是连续多次动态分配内存

这个过程我们可以直观地简单理解成(严格来讲是错误的,本质上真正引发异常的还是一次性分配内存,而剩余内存空间不足)把1次分配大数据量的内存拆分成多次分配小内存空间。比如下面的程序:

1
2
3
4
5
6
7
8
9
10
StringBuffer str = string .Empty;
  str += "hello" ;
  str += " " ;
  str += "world" ;
  str += Environment.NewLine;
  for  ( int  i = 0; i < 24; i++)
  {
      str += str;
  }
  Console.WriteLine(str);

我们利用上面介绍的StringBuffer类来进行字符串拼接。在for循环的时候,程序是以几何级数(2的n次幂)拼接字符串的。所以如果我们循环次数过多,很容就就出现内存不足的异常了。在本地测试的时候,我的电脑到循环24次就出现异常了。如果我们知道可变的(mutable)字符串StringBuilder是如何在托管堆上动态分配内存的,那么这里抛出异常就不难理解了。

 

二、”堆栈爆掉“

StackOverFlowException,字面理解就是”堆栈爆掉“。说起这个异常,大家很容易联想到递归,下面写一段简单代码重现这个异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class  Example
{
     private  string  name;
 
     public  string  Name
     {
         get
         {
             //return name;
             return  Name; //这里造成递归调用
         }
         set  { name = value; }
     }
 
     public  Example()
     {
         name = "stack over flow" ;
     }
     static  void  Main()
     {
         Example obj = new  Example();
         Console.WriteLine(obj.Name);
 
         Console.Read();
     }
 
}

平时我们谈到递归,通常立刻会想到方法的递归调用。这个程序在输出Name属性(属性的本质其实也是方法,这点通过查看IL可以一窥全豹,因为我们知道MSIL中除了类,方法和字段,是没有属性的)的时候发生了递归调用。Name属性的值是通过在get内返回Name(实际上应该是返回name)属性来获取,这样就导致了Name属性的获取发生了无限递归调用(注意,这里所谓”无限“递归调用,字面理解好像是正确的,但是真正递归的层数和你电脑的内存以及CLR有关系,可以肯定不是随心所欲的”无限“了)。避开这种异常的最简单方法就是程序里尽可能地不使用递归(比如通过迭代方法),或者使用优化过了的递归(请参考老赵博客),而对于本文的递归,只要把属性写正确就行了。






本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2010/12/01/1893822.html,如需转载请自行联系原作者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值