| StringBuffer帮你减轻Java的负担
处理文本值是程序员的日常工作,通常用标准的java string类来完成与文本有关的需求。它对于很多小任务确实很适用,但是如果处理的是大型任务,它会大量消耗系统资源。由于这个原因,jdk引入了stringbuffer类以提供一条处理字符串的有效路径。让我们来看看怎样用这个类来提升性能。
为什么不用标准的string?
java string对象是常量字符串。一旦被初始化和付值,它的值和所分配的内存就被固定了。如果硬要改变它的值,将会产生一个包含新值的新string对象。这就是string对象会消耗掉很多资源的原因。下面的代码创建了一个string对象并使用串联(+)符号来为它添加更多字符:
string sample1=new string(“builder.com”);
sample1+=”is”;
sample1+=”the place”;
sample1+=”to be.”;
系统最终会创建四个string对象来完成上面的替换。其中第一个的文本是builder.com。然后每次添加文本时都会创建一个新的对象。
这种方法的问题在于为了这么一个简单的过程而消耗了太多的资源。在这个例子中其影响也许很小(指给出了很少的代码),但是在一个拥有多得多操作的大型应用程序中这样做就会使性能下降。stringbuffer类所要解决的正是这个问题。
用stringbuffer处理字符串 stringbuffer类被设计用与创建和操作动态字符串信息。为该对象分配的内存会自动扩展以容纳新增的文本。有三种方法来创建一个新的stringbuffer对象:使用初始化字符串、设定大小以及使用默认构造函数:
stringbuffer sb=new stringbuffer();
stringbuffer sb=new stringbuffer(30);
stringbuffer sb=new stringbuffer(“builder.com”);
第一行创建了不包含任何文本的对象,默认的容量是16个字符。类的第二个实例也不包含文本,容量是30个字符,最后一行创建了一个拥有初始化值的对象。stringbuffer类位于java.lang基础包中,因此要使用它的话不需要特殊的引入语句。
一旦创建了stringbuffer类的对象,就可以使用stringbuffer类的大量方法和属性。最显著的方法是append,它将文本添加到当前stringbuffer对象内容的结尾。下面的代码示例了append方法的语法:
stringbuffer sb=new stringbuffer();
sb.append(“b”);
sb.append(“u”);
sb.append(“i”);
sb.append(“l”);
sb.append(“d”);
sb.append(“e”);
sb.append(“r”);
sb.append(“.com”);
system.out.println(sb.tostring());
这些代码创建了builder.com字符串并将它送往标准输出,但是只创建了一个对象。如果使用string对象就需要八个以上的对象。注意代码利用了stringbuffer类的tostring方法。这个方法将其内容转换成一个可以被用于输出的字符串对象。它允许操作对应的文本用于输出或数据存储。
append方法有十种重载形式,允许将各种类型的数据添加到对象的末尾。stringbuffer类还提供了处理对象内部数据的方法。
java中String,new String,StringBuffer的思考 java.lang.String类对大家来说最熟悉不过了,我们写java程序很少能不用String的。本文讲述如何正确的使用String,内容主要涉及初始化、串联和比较等操作。 首先我们必须清楚的一点是String类是final类型的,因此你不可以继承这个类、不能修改这个类。我们使用String的时候非常简单,通常都是 String s = "hello",但是Java API中同时提供了一个构造函数为String(String s),因此你也可以这样使用String s = new String("hello",对于后面这样初始化一个String的方式是不推荐的,因为new操作符意味着将会在heap上生成一个新的对象,如果这样的操作发生在一个循环中,那么代价是惨重的。比如 for(int i = 0;i<1000> { String s = new String("hello"; } 这将会创建1000个String类型的对象,由于String类是final的,因此这样的操作事实上是每次都生成了一个新的String对象的。如果你使用String s = "hello";那么就可以实现复用了。 看这两条语句: String password="ok"; String password=new String("ok"; 不同的是,第一条现在内存中创建了"ok"这个String,然后将reference赋给password,如果这个时候还有一条语句String pwd = "ok";那么JVM将不再创建"ok",而是直接将第一个"ok"的reference赋给pwd,也就是说,password和pwd是使用同一块内存,而如果加上String pwd = new String("ok";那JVM将在内存中再创建一块区域放上“ok”这个字符串。 通常对String的比较有两种情况,一个是使用==,另一个是使用equals()方法,注意==是对对象的地址进行比较的,而String中的 equals()方法是覆盖了Object类的方法,并且实现为对String对象的内容的比较。所以String s1 = new String("hello";String s2 = new String("hello",我们对s1和s2进行上述比较的时候,前者应该返回false,因为使用new生成的是两个不同的对象。后者应该返回 true因为他们的内容是一样的,都是"hello"。那么如果我们还有一个String s3 = "hello";他和s1的比较应该是什么样子的呢,答案是s1==s3为false,equals的比较位true。事实上String类是维持着一个 String池的,这个池初始化为空的,当我们String x = "hello"的时候,hello就会被放入这个池中,当我们再次String y = "hello"的时候,他首先去检查池中是否存在一个和hello内容一样的对象,如果存在的话就会把这个引用返回给y,如果不存在的话,就会创建一个并放入到池中。这样就实现了复用。在String有一个方法intern()他可以把String的对象放入到池冲并返回池中的对象。如果我们对s1 (String s1 = new String("hello")调用intern,s1 = s1.intern()这时候,我们再把s1和s3进行“==”的判断,你会发现结果返回true!
String是一个final Class,StringBuffer不是。所以对于 String a = "yacht" ,String b = "yacht1" String c = a + b ; 存在一个对象拷贝构造和解析的消耗问题;对于一个StringBuffer来说,StringBuffer sb = new StringBuffer();sb.append("yacht" ; sb.append("yacht1"; 因为StringBuffer是一个可以实例化的类,而且它的内建机制是维护了一个capacity大小的字符数组,所以它的append操作不存在对象的消耗问题,所以我觉得如果存在String连接这种事情,StringBuffer来做会好很多。 但事情并不是这么简单,看下面代码 String a = "yacht1" + "yacht2" + "yacht3" + "yacht4"; StringBuffer sb = new StringBuffer(); sb.append("yacht1"; sb.append("yacht2"; sb.append("yacht3"; sb.append("yacht4"; String a = sb.toString(); 如果按照我先前说的看法,第一个效率肯定比第二个低,但经过测试不是这样,为什么?这里,我们需要理解程序过程的两个时期,一个是编译时,一个是运行时,在编译时,编译器会对你的程序做出优化,所以第一个的String a会被优化成yacht1yacht2yacht3yacht4,而第二个的StringBuffer只会在运行时才处理。所以效率是不一样的。 如果代码是这样的: String a ; for(int i = 0; i< 100000;i++) a += String.valueOf(i); StringBuffer sb = new StringBuffer(); for(int i = 0; i< 100000;i++) sb.append(i) ; String a = sb.toString(); 如果是这种情况的话,第一个的效率就大大不如蓝色,区别在哪里,就在于运行时和编译时的优化问题上!
效率:String 与 StringBuffer
情景1: (1) String result = "hello" + " world"; (2) StringBuffer result = new String().append("hello".append(" world";
(1) 的效率好于 (2),不要奇怪,这是因为JVM会做如下处理 编译前 String result = "hello" + " world"; 编译后 String result = "hello world";
情景2: (1) public String getString(String s1, String s2) { return s1 + s2; } (2) public String getString(String s1, String s2) { return new StringBuffer().append(s1).append(s2); }
(1) 的效率与 (2) 一样,这是因为JVM会做如下处理 编译前 return s1 + s2; 编译后 return new StringBuffer().append(s1).append(s2);
情景3: (1) String s = "s1"; s += "s2"; s += "s3"; (2) StringBuffer s = new StringBuffer().append("s1".append("s2".append("s3";
(2) 的效率好于(1),因为String是不可变对象,每次"+="操作都会造成构造新的String对象
情景4: (1) StringBuffer s = new StringBuffer(); for (int i = 0; i < 50000; i ++) { s.append("hello"; } (2) StringBuffer s = new StringBuffer(250000); for (int i = 0; i < 50000; i ++) { s.append("hello"; } (2) 的效率好于 (1),因为StringBuffer内部实现是char数组,默认初始化长度为16,每当字符串长度大于char 数组长度的时候,JVM会构造更大的新数组,并将原先的数组内容复制到新数组,(2)避免了复制数组的开销
|