String StringBuffer和StringBuilder
一、总述
String是字符串常量,StringBuffer和StringBuilder是字符串变量;StringBuffer线程安全而StringBuilder是不安全的
二、String与两者的区别
String:
1 public final class String 2 implements java.io.Serializable, Comparable<String>, CharSequence { 3 private final char value[]; 4 } 5
String是不可变类,由final修饰,其底层结构char[ ]也是final,并且其中每个方法都没有对value的内容进行改动,一旦创建就不可修改。
1 String a = "idea"; 2 a = a+"good";
如a = "good"+a,并不是在a的原地址上修改,而是创建了一个新的"ideagood"对象,并把引用给a。
StringBuffer和StringBuilder:
1 public final class StringBuffer extends AbstractStringBuilder 2 implements java.io.Serializable, CharSequence
1 public final class StringBuilder extends AbstractStringBuilder 2 implements java.io.Serializable, CharSequence 3
StringBuffer和StringBuilder都继承自AbstractStringBuilder。
1 abstract class AbstractStringBuilder implements Appendable, CharSequence { 2 char[] value; 3 } 4
其底层char[ ]数组并未被final修饰是字符串变量,上述a = "good"+a,实际操作为a = new StringBuffer(a).append("good").toString,它的修改是在原地址上进行的。
三、StringBuffer和StringBuilder区别
AbstractStringBuilder的append方法,ensureCapacityInternal( )函数会对char[ ]数组容量进行校验,不够会进行扩容:
1 public AbstractStringBuilder append(char[] str) { 2 int len = str.length; 3 ensureCapacityInternal(count + len); 4 System.arraycopy(str, 0, value, count, len); 5 count += len; 6 return this; 7 }
StringBuffer的所有public方法均采用Sychronized修饰,因此线程安全,但相比之下效率低于StringBuilder
1 @Override 2 public synchronized StringBuffer append(char[] str) { 3 toStringCache = null; 4 super.append(str); 5 return this; 6 }
StringBuilder直接使用父类方法,不具备线程安全,但单线程下效率可观
1 @Override 2 public StringBuilder append(char[] str) { 3 super.append(str); 4 return this; 5 } 6
四、HashSet的唯一性问题
Set接口存入对象具有唯一性,但也存在问题:
1 HashSet<StringBuilder> set2 = new HashSet<>(); 2 StringBuilder sb1 = new StringBuilder("abc"); 3 StringBuilder sb2 = new StringBuilder("abc"); 4 System.out.println(sb1 == sb2);//false 5 6 set2.add(sb1); 7 set2.add(sb2); 8 9 Iterator<StringBuilder> it2 = set2.iterator(); 10 while (it2.hasNext()) 11 System.out.println(it2.next());//"abc" "abc
sb1和sb2内容相同的不同对象,HashSet存入两个相同的值,打破了唯一性;
1 HashSet<String> set1 = new HashSet<>(); 2 String s1 = new String("abc"); 3 String s2 = "abc"; 4 System.out.println(s1 == s2);//"false" 5 set1.add(s1); 6 set1.add(s2); 7 Iterator<String> it1 = set1.iterator(); 8 while (it1.hasNext()) 9 System.out.println(it1.next());//"abc"
s1和s2内容相同的不同对象,但set中只存入了一个值。
原因:HashSet底层是HashMap,而HashMap对对象的判断:
1 if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) 2 break;
是通过hashcode和equals( )方法,String重写了hash函数和equals方法,不再是根据对象地址进行计算和判断,而是根据对象内容进行计算,因此s1和s2虽然地址不同,但hashcode相同,equals为true。而StringBuilder没有重写这两个方法,因此sb1和sb2尽管内容相同,因为地址不同,所以hashcode不同,equals为false。
1 public boolean equals(Object anObject) { 2 if (this == anObject) { 3 return true; 4 } 5 if (anObject instanceof String) { 6 String anotherString = (String)anObject; 7 int n = value.length; 8 if (n == anotherString.value.length) { 9 char v1[] = value; 10 char v2[] = anotherString.value; 11 int i = 0; 12 while (n-- != 0) { 13 if (v1[i] != v2[i]) 14 return false; 15 i++; 16 } 17 return true; 18 } 19 } 20 return false; 21 }
1 public int hashCode() { 2 int h = hash; 3 if (h == 0 && value.length > 0) { 4 char val[] = value; 5 for (int i = 0; i < value.length; i++) { 6 h = 31 * h + val[i]; 7 } 8 hash = h; 9 } 10 return h; 11 }